You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3105 lines
127 KiB

2 years ago
  1. /**
  2. * @license A class to parse color values
  3. * @author Stoyan Stefanov <sstoo@gmail.com>
  4. * @link http://www.phpied.com/rgb-color-parser-in-javascript/
  5. * Use it if you like it
  6. *
  7. */
  8. function RGBColor(color_string) {
  9. this.ok = false;
  10. // strip any leading #
  11. if (color_string.charAt(0) == '#') { // remove # if any
  12. color_string = color_string.substr(1, 6);
  13. }
  14. color_string = color_string.replace(/ /g, '');
  15. color_string = color_string.toLowerCase();
  16. // before getting into regexps, try simple matches
  17. // and overwrite the input
  18. var simple_colors = {
  19. aliceblue: 'f0f8ff',
  20. antiquewhite: 'faebd7',
  21. aqua: '00ffff',
  22. aquamarine: '7fffd4',
  23. azure: 'f0ffff',
  24. beige: 'f5f5dc',
  25. bisque: 'ffe4c4',
  26. black: '000000',
  27. blanchedalmond: 'ffebcd',
  28. blue: '0000ff',
  29. blueviolet: '8a2be2',
  30. brown: 'a52a2a',
  31. burlywood: 'deb887',
  32. cadetblue: '5f9ea0',
  33. chartreuse: '7fff00',
  34. chocolate: 'd2691e',
  35. coral: 'ff7f50',
  36. cornflowerblue: '6495ed',
  37. cornsilk: 'fff8dc',
  38. crimson: 'dc143c',
  39. cyan: '00ffff',
  40. darkblue: '00008b',
  41. darkcyan: '008b8b',
  42. darkgoldenrod: 'b8860b',
  43. darkgray: 'a9a9a9',
  44. darkgreen: '006400',
  45. darkkhaki: 'bdb76b',
  46. darkmagenta: '8b008b',
  47. darkolivegreen: '556b2f',
  48. darkorange: 'ff8c00',
  49. darkorchid: '9932cc',
  50. darkred: '8b0000',
  51. darksalmon: 'e9967a',
  52. darkseagreen: '8fbc8f',
  53. darkslateblue: '483d8b',
  54. darkslategray: '2f4f4f',
  55. darkturquoise: '00ced1',
  56. darkviolet: '9400d3',
  57. deeppink: 'ff1493',
  58. deepskyblue: '00bfff',
  59. dimgray: '696969',
  60. dodgerblue: '1e90ff',
  61. feldspar: 'd19275',
  62. firebrick: 'b22222',
  63. floralwhite: 'fffaf0',
  64. forestgreen: '228b22',
  65. fuchsia: 'ff00ff',
  66. gainsboro: 'dcdcdc',
  67. ghostwhite: 'f8f8ff',
  68. gold: 'ffd700',
  69. goldenrod: 'daa520',
  70. gray: '808080',
  71. green: '008000',
  72. greenyellow: 'adff2f',
  73. honeydew: 'f0fff0',
  74. hotpink: 'ff69b4',
  75. indianred: 'cd5c5c',
  76. indigo: '4b0082',
  77. ivory: 'fffff0',
  78. khaki: 'f0e68c',
  79. lavender: 'e6e6fa',
  80. lavenderblush: 'fff0f5',
  81. lawngreen: '7cfc00',
  82. lemonchiffon: 'fffacd',
  83. lightblue: 'add8e6',
  84. lightcoral: 'f08080',
  85. lightcyan: 'e0ffff',
  86. lightgoldenrodyellow: 'fafad2',
  87. lightgrey: 'd3d3d3',
  88. lightgreen: '90ee90',
  89. lightpink: 'ffb6c1',
  90. lightsalmon: 'ffa07a',
  91. lightseagreen: '20b2aa',
  92. lightskyblue: '87cefa',
  93. lightslateblue: '8470ff',
  94. lightslategray: '778899',
  95. lightsteelblue: 'b0c4de',
  96. lightyellow: 'ffffe0',
  97. lime: '00ff00',
  98. limegreen: '32cd32',
  99. linen: 'faf0e6',
  100. magenta: 'ff00ff',
  101. maroon: '800000',
  102. mediumaquamarine: '66cdaa',
  103. mediumblue: '0000cd',
  104. mediumorchid: 'ba55d3',
  105. mediumpurple: '9370d8',
  106. mediumseagreen: '3cb371',
  107. mediumslateblue: '7b68ee',
  108. mediumspringgreen: '00fa9a',
  109. mediumturquoise: '48d1cc',
  110. mediumvioletred: 'c71585',
  111. midnightblue: '191970',
  112. mintcream: 'f5fffa',
  113. mistyrose: 'ffe4e1',
  114. moccasin: 'ffe4b5',
  115. navajowhite: 'ffdead',
  116. navy: '000080',
  117. oldlace: 'fdf5e6',
  118. olive: '808000',
  119. olivedrab: '6b8e23',
  120. orange: 'ffa500',
  121. orangered: 'ff4500',
  122. orchid: 'da70d6',
  123. palegoldenrod: 'eee8aa',
  124. palegreen: '98fb98',
  125. paleturquoise: 'afeeee',
  126. palevioletred: 'd87093',
  127. papayawhip: 'ffefd5',
  128. peachpuff: 'ffdab9',
  129. peru: 'cd853f',
  130. pink: 'ffc0cb',
  131. plum: 'dda0dd',
  132. powderblue: 'b0e0e6',
  133. purple: '800080',
  134. red: 'ff0000',
  135. rosybrown: 'bc8f8f',
  136. royalblue: '4169e1',
  137. saddlebrown: '8b4513',
  138. salmon: 'fa8072',
  139. sandybrown: 'f4a460',
  140. seagreen: '2e8b57',
  141. seashell: 'fff5ee',
  142. sienna: 'a0522d',
  143. silver: 'c0c0c0',
  144. skyblue: '87ceeb',
  145. slateblue: '6a5acd',
  146. slategray: '708090',
  147. snow: 'fffafa',
  148. springgreen: '00ff7f',
  149. steelblue: '4682b4',
  150. tan: 'd2b48c',
  151. teal: '008080',
  152. thistle: 'd8bfd8',
  153. tomato: 'ff6347',
  154. turquoise: '40e0d0',
  155. violet: 'ee82ee',
  156. violetred: 'd02090',
  157. wheat: 'f5deb3',
  158. white: 'ffffff',
  159. whitesmoke: 'f5f5f5',
  160. yellow: 'ffff00',
  161. yellowgreen: '9acd32'
  162. };
  163. for (var key in simple_colors) {
  164. if (color_string == key) {
  165. color_string = simple_colors[key];
  166. }
  167. }
  168. // emd of simple type-in colors
  169. // array of color definition objects
  170. var color_defs = [
  171. {
  172. re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,
  173. example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'],
  174. process: function (bits) {
  175. return [
  176. parseInt(bits[1]),
  177. parseInt(bits[2]),
  178. parseInt(bits[3])
  179. ];
  180. }
  181. },
  182. {
  183. re: /^(\w{2})(\w{2})(\w{2})$/,
  184. example: ['#00ff00', '336699'],
  185. process: function (bits) {
  186. return [
  187. parseInt(bits[1], 16),
  188. parseInt(bits[2], 16),
  189. parseInt(bits[3], 16)
  190. ];
  191. }
  192. },
  193. {
  194. re: /^(\w{1})(\w{1})(\w{1})$/,
  195. example: ['#fb0', 'f0f'],
  196. process: function (bits) {
  197. return [
  198. parseInt(bits[1] + bits[1], 16),
  199. parseInt(bits[2] + bits[2], 16),
  200. parseInt(bits[3] + bits[3], 16)
  201. ];
  202. }
  203. }
  204. ];
  205. // search through the definitions to find a match
  206. for (var i = 0; i < color_defs.length; i++) {
  207. var re = color_defs[i].re;
  208. var processor = color_defs[i].process;
  209. var bits = re.exec(color_string);
  210. if (bits) {
  211. channels = processor(bits);
  212. this.r = channels[0];
  213. this.g = channels[1];
  214. this.b = channels[2];
  215. this.ok = true;
  216. }
  217. }
  218. // validate/cleanup values
  219. this.r = (this.r < 0 || isNaN(this.r)) ? 0 : ((this.r > 255) ? 255 : this.r);
  220. this.g = (this.g < 0 || isNaN(this.g)) ? 0 : ((this.g > 255) ? 255 : this.g);
  221. this.b = (this.b < 0 || isNaN(this.b)) ? 0 : ((this.b > 255) ? 255 : this.b);
  222. // some getters
  223. this.toRGB = function () {
  224. return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')';
  225. }
  226. this.toHex = function () {
  227. var r = this.r.toString(16);
  228. var g = this.g.toString(16);
  229. var b = this.b.toString(16);
  230. if (r.length == 1) r = '0' + r;
  231. if (g.length == 1) g = '0' + g;
  232. if (b.length == 1) b = '0' + b;
  233. return '#' + r + g + b;
  234. }
  235. // help
  236. this.getHelpXML = function () {
  237. var examples = new Array();
  238. // add regexps
  239. for (var i = 0; i < color_defs.length; i++) {
  240. var example = color_defs[i].example;
  241. for (var j = 0; j < example.length; j++) {
  242. examples[examples.length] = example[j];
  243. }
  244. }
  245. // add type-in colors
  246. for (var sc in simple_colors) {
  247. examples[examples.length] = sc;
  248. }
  249. var xml = document.createElement('ul');
  250. xml.setAttribute('id', 'rgbcolor-examples');
  251. for (var i = 0; i < examples.length; i++) {
  252. try {
  253. var list_item = document.createElement('li');
  254. var list_color = new RGBColor(examples[i]);
  255. var example_div = document.createElement('div');
  256. example_div.style.cssText =
  257. 'margin: 3px; '
  258. + 'border: 1px solid black; '
  259. + 'background:' + list_color.toHex() + '; '
  260. + 'color:' + list_color.toHex()
  261. ;
  262. example_div.appendChild(document.createTextNode('test'));
  263. var list_item_value = document.createTextNode(
  264. ' ' + examples[i] + ' -> ' + list_color.toRGB() + ' -> ' + list_color.toHex()
  265. );
  266. list_item.appendChild(example_div);
  267. list_item.appendChild(list_item_value);
  268. xml.appendChild(list_item);
  269. } catch (e) { }
  270. }
  271. return xml;
  272. }
  273. }
  274. /**
  275. * @license canvg.js - Javascript SVG parser and renderer on Canvas
  276. * MIT Licensed
  277. * Gabe Lerner (gabelerner@gmail.com)
  278. * http://code.google.com/p/canvg/
  279. *
  280. * Requires: rgbcolor.js - http://www.phpied.com/rgb-color-parser-in-javascript/
  281. *
  282. */
  283. if (!window.console) {
  284. window.console = {};
  285. window.console.log = function (str) { };
  286. window.console.dir = function (str) { };
  287. }
  288. if (!Array.prototype.indexOf) {
  289. Array.prototype.indexOf = function (obj) {
  290. for (var i = 0; i < this.length; i++) {
  291. if (this[i] == obj) {
  292. return i;
  293. }
  294. }
  295. return -1;
  296. }
  297. }
  298. (function () {
  299. // canvg(target, s)
  300. // empty parameters: replace all 'svg' elements on page with 'canvas' elements
  301. // target: canvas element or the id of a canvas element
  302. // s: svg string, url to svg file, or xml document
  303. // opts: optional hash of options
  304. // ignoreMouse: true => ignore mouse events
  305. // ignoreAnimation: true => ignore animations
  306. // ignoreDimensions: true => does not try to resize canvas
  307. // ignoreClear: true => does not clear canvas
  308. // offsetX: int => draws at a x offset
  309. // offsetY: int => draws at a y offset
  310. // scaleWidth: int => scales horizontally to width
  311. // scaleHeight: int => scales vertically to height
  312. // renderCallback: function => will call the function after the first render is completed
  313. // forceRedraw: function => will call the function on every frame, if it returns true, will redraw
  314. this.canvg = function (target, s, opts) {
  315. // no parameters
  316. if (target == null && s == null && opts == null) {
  317. var svgTags = document.getElementsByTagName('svg');
  318. for (var i = 0; i < svgTags.length; i++) {
  319. var svgTag = svgTags[i];
  320. var c = document.createElement('canvas');
  321. c.width = svgTag.clientWidth;
  322. c.height = svgTag.clientHeight;
  323. svgTag.parentNode.insertBefore(c, svgTag);
  324. svgTag.parentNode.removeChild(svgTag);
  325. var div = document.createElement('div');
  326. div.appendChild(svgTag);
  327. canvg(c, div.innerHTML);
  328. }
  329. return;
  330. }
  331. opts = opts || {};
  332. if (typeof target == 'string') {
  333. target = document.getElementById(target);
  334. }
  335. // reuse class per canvas
  336. var svg;
  337. if (target.svg == null) {
  338. svg = build();
  339. target.svg = svg;
  340. }
  341. else {
  342. svg = target.svg;
  343. svg.stop();
  344. }
  345. svg.opts = opts;
  346. var ctx = target.getContext('2d');
  347. if (typeof (s.documentElement) != 'undefined') {
  348. // load from xml doc
  349. svg.loadXmlDoc(ctx, s);
  350. }
  351. else if (s.substr(0, 1) == '<') {
  352. // load from xml string
  353. svg.loadXml(ctx, s);
  354. }
  355. else {
  356. // load from url
  357. svg.load(ctx, s);
  358. }
  359. }
  360. function build() {
  361. var svg = {};
  362. svg.FRAMERATE = 30;
  363. svg.MAX_VIRTUAL_PIXELS = 30000;
  364. // globals
  365. svg.init = function (ctx) {
  366. svg.Definitions = {};
  367. svg.Styles = {};
  368. svg.Animations = [];
  369. svg.Images = [];
  370. svg.ctx = ctx;
  371. svg.ViewPort = new (function () {
  372. this.viewPorts = [];
  373. this.Clear = function () { this.viewPorts = []; }
  374. this.SetCurrent = function (width, height) { this.viewPorts.push({ width: width, height: height }); }
  375. this.RemoveCurrent = function () { this.viewPorts.pop(); }
  376. this.Current = function () { return this.viewPorts[this.viewPorts.length - 1]; }
  377. this.width = function () { return this.Current().width; }
  378. this.height = function () { return this.Current().height; }
  379. this.ComputeSize = function (d) {
  380. if (d != null && typeof (d) == 'number') return d;
  381. if (d == 'x') return this.width();
  382. if (d == 'y') return this.height();
  383. return Math.sqrt(Math.pow(this.width(), 2) + Math.pow(this.height(), 2)) / Math.sqrt(2);
  384. }
  385. });
  386. }
  387. svg.init();
  388. // images loaded
  389. svg.ImagesLoaded = function () {
  390. for (var i = 0; i < svg.Images.length; i++) {
  391. if (!svg.Images[i].loaded) return false;
  392. }
  393. return true;
  394. }
  395. // trim
  396. svg.trim = function (s) { return s.replace(/^\s+|\s+$/g, ''); }
  397. // compress spaces
  398. svg.compressSpaces = function (s) { return s.replace(/[\s\r\t\n]+/gm, ' '); }
  399. // ajax
  400. svg.ajax = function (url) {
  401. var AJAX;
  402. if (window.XMLHttpRequest) { AJAX = new XMLHttpRequest(); }
  403. else { AJAX = new ActiveXObject('Microsoft.XMLHTTP'); }
  404. if (AJAX) {
  405. AJAX.open('GET', url, false);
  406. AJAX.send(null);
  407. return AJAX.responseText;
  408. }
  409. return null;
  410. }
  411. // parse xml
  412. svg.parseXml = function (xml) {
  413. if (window.DOMParser) {
  414. var parser = new DOMParser();
  415. return parser.parseFromString(xml, 'text/xml');
  416. }
  417. else {
  418. xml = xml.replace(/<!DOCTYPE svg[^>]*>/, '');
  419. var xmlDoc = new ActiveXObject('Microsoft.XMLDOM');
  420. xmlDoc.async = 'false';
  421. xmlDoc.loadXML(xml);
  422. return xmlDoc;
  423. }
  424. }
  425. svg.Property = function (name, value) {
  426. this.name = name;
  427. this.value = value;
  428. this.hasValue = function () {
  429. return (this.value != null && this.value !== '');
  430. }
  431. // return the numerical value of the property
  432. this.numValue = function () {
  433. if (!this.hasValue()) return 0;
  434. var n = parseFloat(this.value);
  435. if ((this.value + '').match(/%$/)) {
  436. n = n / 100.0;
  437. }
  438. return n;
  439. }
  440. this.valueOrDefault = function (def) {
  441. if (this.hasValue()) return this.value;
  442. return def;
  443. }
  444. this.numValueOrDefault = function (def) {
  445. if (this.hasValue()) return this.numValue();
  446. return def;
  447. }
  448. /* EXTENSIONS */
  449. var that = this;
  450. // color extensions
  451. this.Color = {
  452. // augment the current color value with the opacity
  453. addOpacity: function (opacity) {
  454. var newValue = that.value;
  455. if (opacity != null && opacity != '') {
  456. var color = new RGBColor(that.value);
  457. if (color.ok) {
  458. newValue = 'rgba(' + color.r + ', ' + color.g + ', ' + color.b + ', ' + opacity + ')';
  459. }
  460. }
  461. return new svg.Property(that.name, newValue);
  462. }
  463. }
  464. // definition extensions
  465. this.Definition = {
  466. // get the definition from the definitions table
  467. getDefinition: function () {
  468. var name = that.value.replace(/^(url\()?#([^\)]+)\)?$/, '$2');
  469. return svg.Definitions[name];
  470. },
  471. isUrl: function () {
  472. return that.value.indexOf('url(') == 0
  473. },
  474. getFillStyle: function (e) {
  475. var def = this.getDefinition();
  476. // gradient
  477. if (def != null && def.createGradient) {
  478. return def.createGradient(svg.ctx, e);
  479. }
  480. // pattern
  481. if (def != null && def.createPattern) {
  482. return def.createPattern(svg.ctx, e);
  483. }
  484. return null;
  485. }
  486. }
  487. // length extensions
  488. this.Length = {
  489. DPI: function (viewPort) {
  490. return 96.0; // TODO: compute?
  491. },
  492. EM: function (viewPort) {
  493. var em = 12;
  494. var fontSize = new svg.Property('fontSize', svg.Font.Parse(svg.ctx.font).fontSize);
  495. if (fontSize.hasValue()) em = fontSize.Length.toPixels(viewPort);
  496. return em;
  497. },
  498. // get the length as pixels
  499. toPixels: function (viewPort) {
  500. if (!that.hasValue()) return 0;
  501. var s = that.value + '';
  502. if (s.match(/em$/)) return that.numValue() * this.EM(viewPort);
  503. if (s.match(/ex$/)) return that.numValue() * this.EM(viewPort) / 2.0;
  504. if (s.match(/px$/)) return that.numValue();
  505. if (s.match(/pt$/)) return that.numValue() * 1.25;
  506. if (s.match(/pc$/)) return that.numValue() * 15;
  507. if (s.match(/cm$/)) return that.numValue() * this.DPI(viewPort) / 2.54;
  508. if (s.match(/mm$/)) return that.numValue() * this.DPI(viewPort) / 25.4;
  509. if (s.match(/in$/)) return that.numValue() * this.DPI(viewPort);
  510. if (s.match(/%$/)) return that.numValue() * svg.ViewPort.ComputeSize(viewPort);
  511. return that.numValue();
  512. }
  513. }
  514. // time extensions
  515. this.Time = {
  516. // get the time as milliseconds
  517. toMilliseconds: function () {
  518. if (!that.hasValue()) return 0;
  519. var s = that.value + '';
  520. if (s.match(/s$/)) return that.numValue() * 1000;
  521. if (s.match(/ms$/)) return that.numValue();
  522. return that.numValue();
  523. }
  524. }
  525. // angle extensions
  526. this.Angle = {
  527. // get the angle as radians
  528. toRadians: function () {
  529. if (!that.hasValue()) return 0;
  530. var s = that.value + '';
  531. if (s.match(/deg$/)) return that.numValue() * (Math.PI / 180.0);
  532. if (s.match(/grad$/)) return that.numValue() * (Math.PI / 200.0);
  533. if (s.match(/rad$/)) return that.numValue();
  534. return that.numValue() * (Math.PI / 180.0);
  535. }
  536. }
  537. }
  538. // fonts
  539. svg.Font = new (function () {
  540. this.Styles = ['normal', 'italic', 'oblique', 'inherit'];
  541. this.Variants = ['normal', 'small-caps', 'inherit'];
  542. this.Weights = ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900', 'inherit'];
  543. this.CreateFont = function (fontStyle, fontVariant, fontWeight, fontSize, fontFamily, inherit) {
  544. var f = inherit != null ? this.Parse(inherit) : this.CreateFont('', '', '', '', '', svg.ctx.font);
  545. return {
  546. fontFamily: fontFamily || f.fontFamily,
  547. fontSize: fontSize || f.fontSize,
  548. fontStyle: fontStyle || f.fontStyle,
  549. fontWeight: fontWeight || f.fontWeight,
  550. fontVariant: fontVariant || f.fontVariant,
  551. toString: function () { return [this.fontStyle, this.fontVariant, this.fontWeight, this.fontSize, this.fontFamily].join(' ') }
  552. }
  553. }
  554. var that = this;
  555. this.Parse = function (s) {
  556. var f = {};
  557. var d = svg.trim(svg.compressSpaces(s || '')).split(' ');
  558. var set = { fontSize: false, fontStyle: false, fontWeight: false, fontVariant: false }
  559. var ff = '';
  560. for (var i = 0; i < d.length; i++) {
  561. if (!set.fontStyle && that.Styles.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontStyle = d[i]; set.fontStyle = true; }
  562. else if (!set.fontVariant && that.Variants.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontVariant = d[i]; set.fontStyle = set.fontVariant = true; }
  563. else if (!set.fontWeight && that.Weights.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontWeight = d[i]; set.fontStyle = set.fontVariant = set.fontWeight = true; }
  564. else if (!set.fontSize) { if (d[i] != 'inherit') f.fontSize = d[i].split('/')[0]; set.fontStyle = set.fontVariant = set.fontWeight = set.fontSize = true; }
  565. else { if (d[i] != 'inherit') ff += d[i]; }
  566. } if (ff != '') f.fontFamily = ff;
  567. return f;
  568. }
  569. });
  570. // points and paths
  571. svg.ToNumberArray = function (s) {
  572. var a = svg.trim(svg.compressSpaces((s || '').replace(/,/g, ' '))).split(' ');
  573. for (var i = 0; i < a.length; i++) {
  574. a[i] = parseFloat(a[i]);
  575. }
  576. return a;
  577. }
  578. svg.Point = function (x, y) {
  579. this.x = x;
  580. this.y = y;
  581. this.angleTo = function (p) {
  582. return Math.atan2(p.y - this.y, p.x - this.x);
  583. }
  584. this.applyTransform = function (v) {
  585. var xp = this.x * v[0] + this.y * v[2] + v[4];
  586. var yp = this.x * v[1] + this.y * v[3] + v[5];
  587. this.x = xp;
  588. this.y = yp;
  589. }
  590. }
  591. svg.CreatePoint = function (s) {
  592. var a = svg.ToNumberArray(s);
  593. return new svg.Point(a[0], a[1]);
  594. }
  595. svg.CreatePath = function (s) {
  596. var a = svg.ToNumberArray(s);
  597. var path = [];
  598. for (var i = 0; i < a.length; i += 2) {
  599. path.push(new svg.Point(a[i], a[i + 1]));
  600. }
  601. return path;
  602. }
  603. // bounding box
  604. svg.BoundingBox = function (x1, y1, x2, y2) { // pass in initial points if you want
  605. this.x1 = Number.NaN;
  606. this.y1 = Number.NaN;
  607. this.x2 = Number.NaN;
  608. this.y2 = Number.NaN;
  609. this.x = function () { return this.x1; }
  610. this.y = function () { return this.y1; }
  611. this.width = function () { return this.x2 - this.x1; }
  612. this.height = function () { return this.y2 - this.y1; }
  613. this.addPoint = function (x, y) {
  614. if (x != null) {
  615. if (isNaN(this.x1) || isNaN(this.x2)) {
  616. this.x1 = x;
  617. this.x2 = x;
  618. }
  619. if (x < this.x1) this.x1 = x;
  620. if (x > this.x2) this.x2 = x;
  621. }
  622. if (y != null) {
  623. if (isNaN(this.y1) || isNaN(this.y2)) {
  624. this.y1 = y;
  625. this.y2 = y;
  626. }
  627. if (y < this.y1) this.y1 = y;
  628. if (y > this.y2) this.y2 = y;
  629. }
  630. }
  631. this.addX = function (x) { this.addPoint(x, null); }
  632. this.addY = function (y) { this.addPoint(null, y); }
  633. this.addBoundingBox = function (bb) {
  634. this.addPoint(bb.x1, bb.y1);
  635. this.addPoint(bb.x2, bb.y2);
  636. }
  637. this.addQuadraticCurve = function (p0x, p0y, p1x, p1y, p2x, p2y) {
  638. var cp1x = p0x + 2 / 3 * (p1x - p0x); // CP1 = QP0 + 2/3 *(QP1-QP0)
  639. var cp1y = p0y + 2 / 3 * (p1y - p0y); // CP1 = QP0 + 2/3 *(QP1-QP0)
  640. var cp2x = cp1x + 1 / 3 * (p2x - p0x); // CP2 = CP1 + 1/3 *(QP2-QP0)
  641. var cp2y = cp1y + 1 / 3 * (p2y - p0y); // CP2 = CP1 + 1/3 *(QP2-QP0)
  642. this.addBezierCurve(p0x, p0y, cp1x, cp2x, cp1y, cp2y, p2x, p2y);
  643. }
  644. this.addBezierCurve = function (p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) {
  645. // from http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
  646. var p0 = [p0x, p0y], p1 = [p1x, p1y], p2 = [p2x, p2y], p3 = [p3x, p3y];
  647. this.addPoint(p0[0], p0[1]);
  648. this.addPoint(p3[0], p3[1]);
  649. for (i = 0; i <= 1; i++) {
  650. var f = function (t) {
  651. return Math.pow(1 - t, 3) * p0[i]
  652. + 3 * Math.pow(1 - t, 2) * t * p1[i]
  653. + 3 * (1 - t) * Math.pow(t, 2) * p2[i]
  654. + Math.pow(t, 3) * p3[i];
  655. }
  656. var b = 6 * p0[i] - 12 * p1[i] + 6 * p2[i];
  657. var a = -3 * p0[i] + 9 * p1[i] - 9 * p2[i] + 3 * p3[i];
  658. var c = 3 * p1[i] - 3 * p0[i];
  659. if (a == 0) {
  660. if (b == 0) continue;
  661. var t = -c / b;
  662. if (0 < t && t < 1) {
  663. if (i == 0) this.addX(f(t));
  664. if (i == 1) this.addY(f(t));
  665. }
  666. continue;
  667. }
  668. var b2ac = Math.pow(b, 2) - 4 * c * a;
  669. if (b2ac < 0) continue;
  670. var t1 = (-b + Math.sqrt(b2ac)) / (2 * a);
  671. if (0 < t1 && t1 < 1) {
  672. if (i == 0) this.addX(f(t1));
  673. if (i == 1) this.addY(f(t1));
  674. }
  675. var t2 = (-b - Math.sqrt(b2ac)) / (2 * a);
  676. if (0 < t2 && t2 < 1) {
  677. if (i == 0) this.addX(f(t2));
  678. if (i == 1) this.addY(f(t2));
  679. }
  680. }
  681. }
  682. this.isPointInBox = function (x, y) {
  683. return (this.x1 <= x && x <= this.x2 && this.y1 <= y && y <= this.y2);
  684. }
  685. this.addPoint(x1, y1);
  686. this.addPoint(x2, y2);
  687. }
  688. // transforms
  689. svg.Transform = function (v) {
  690. var that = this;
  691. this.Type = {}
  692. // translate
  693. this.Type.translate = function (s) {
  694. this.p = svg.CreatePoint(s);
  695. this.apply = function (ctx) {
  696. ctx.translate(this.p.x || 0.0, this.p.y || 0.0);
  697. }
  698. this.applyToPoint = function (p) {
  699. p.applyTransform([1, 0, 0, 1, this.p.x || 0.0, this.p.y || 0.0]);
  700. }
  701. }
  702. // rotate
  703. this.Type.rotate = function (s) {
  704. var a = svg.ToNumberArray(s);
  705. this.angle = new svg.Property('angle', a[0]);
  706. this.cx = a[1] || 0;
  707. this.cy = a[2] || 0;
  708. this.apply = function (ctx) {
  709. ctx.translate(this.cx, this.cy);
  710. ctx.rotate(this.angle.Angle.toRadians());
  711. ctx.translate(-this.cx, -this.cy);
  712. }
  713. this.applyToPoint = function (p) {
  714. var a = this.angle.Angle.toRadians();
  715. p.applyTransform([1, 0, 0, 1, this.p.x || 0.0, this.p.y || 0.0]);
  716. p.applyTransform([Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 0, 0]);
  717. p.applyTransform([1, 0, 0, 1, -this.p.x || 0.0, -this.p.y || 0.0]);
  718. }
  719. }
  720. this.Type.scale = function (s) {
  721. this.p = svg.CreatePoint(s);
  722. this.apply = function (ctx) {
  723. ctx.scale(this.p.x || 1.0, this.p.y || this.p.x || 1.0);
  724. }
  725. this.applyToPoint = function (p) {
  726. p.applyTransform([this.p.x || 0.0, 0, 0, this.p.y || 0.0, 0, 0]);
  727. }
  728. }
  729. this.Type.matrix = function (s) {
  730. this.m = svg.ToNumberArray(s);
  731. this.apply = function (ctx) {
  732. ctx.transform(this.m[0], this.m[1], this.m[2], this.m[3], this.m[4], this.m[5]);
  733. }
  734. this.applyToPoint = function (p) {
  735. p.applyTransform(this.m);
  736. }
  737. }
  738. this.Type.SkewBase = function (s) {
  739. this.base = that.Type.matrix;
  740. this.base(s);
  741. this.angle = new svg.Property('angle', s);
  742. }
  743. this.Type.SkewBase.prototype = new this.Type.matrix;
  744. this.Type.skewX = function (s) {
  745. this.base = that.Type.SkewBase;
  746. this.base(s);
  747. this.m = [1, 0, Math.tan(this.angle.Angle.toRadians()), 1, 0, 0];
  748. }
  749. this.Type.skewX.prototype = new this.Type.SkewBase;
  750. this.Type.skewY = function (s) {
  751. this.base = that.Type.SkewBase;
  752. this.base(s);
  753. this.m = [1, Math.tan(this.angle.Angle.toRadians()), 0, 1, 0, 0];
  754. }
  755. this.Type.skewY.prototype = new this.Type.SkewBase;
  756. this.transforms = [];
  757. this.apply = function (ctx) {
  758. for (var i = 0; i < this.transforms.length; i++) {
  759. this.transforms[i].apply(ctx);
  760. }
  761. }
  762. this.applyToPoint = function (p) {
  763. for (var i = 0; i < this.transforms.length; i++) {
  764. this.transforms[i].applyToPoint(p);
  765. }
  766. }
  767. var data = svg.trim(svg.compressSpaces(v)).split(/\s(?=[a-z])/);
  768. for (var i = 0; i < data.length; i++) {
  769. var type = data[i].split('(')[0];
  770. var s = data[i].split('(')[1].replace(')', '');
  771. var transform = new this.Type[type](s);
  772. this.transforms.push(transform);
  773. }
  774. }
  775. // aspect ratio
  776. svg.AspectRatio = function (ctx, aspectRatio, width, desiredWidth, height, desiredHeight, minX, minY, refX, refY) {
  777. // aspect ratio - http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute
  778. aspectRatio = svg.compressSpaces(aspectRatio);
  779. aspectRatio = aspectRatio.replace(/^defer\s/, ''); // ignore defer
  780. var align = aspectRatio.split(' ')[0] || 'xMidYMid';
  781. var meetOrSlice = aspectRatio.split(' ')[1] || 'meet';
  782. // calculate scale
  783. var scaleX = width / desiredWidth;
  784. var scaleY = height / desiredHeight;
  785. var scaleMin = Math.min(scaleX, scaleY);
  786. var scaleMax = Math.max(scaleX, scaleY);
  787. if (meetOrSlice == 'meet') { desiredWidth *= scaleMin; desiredHeight *= scaleMin; }
  788. if (meetOrSlice == 'slice') { desiredWidth *= scaleMax; desiredHeight *= scaleMax; }
  789. refX = new svg.Property('refX', refX);
  790. refY = new svg.Property('refY', refY);
  791. if (refX.hasValue() && refY.hasValue()) {
  792. ctx.translate(-scaleMin * refX.Length.toPixels('x'), -scaleMin * refY.Length.toPixels('y'));
  793. }
  794. else {
  795. // align
  796. if (align.match(/^xMid/) && ((meetOrSlice == 'meet' && scaleMin == scaleY) || (meetOrSlice == 'slice' && scaleMax == scaleY))) ctx.translate(width / 2.0 - desiredWidth / 2.0, 0);
  797. if (align.match(/YMid$/) && ((meetOrSlice == 'meet' && scaleMin == scaleX) || (meetOrSlice == 'slice' && scaleMax == scaleX))) ctx.translate(0, height / 2.0 - desiredHeight / 2.0);
  798. if (align.match(/^xMax/) && ((meetOrSlice == 'meet' && scaleMin == scaleY) || (meetOrSlice == 'slice' && scaleMax == scaleY))) ctx.translate(width - desiredWidth, 0);
  799. if (align.match(/YMax$/) && ((meetOrSlice == 'meet' && scaleMin == scaleX) || (meetOrSlice == 'slice' && scaleMax == scaleX))) ctx.translate(0, height - desiredHeight);
  800. }
  801. // scale
  802. if (align == 'none') ctx.scale(scaleX, scaleY);
  803. else if (meetOrSlice == 'meet') ctx.scale(scaleMin, scaleMin);
  804. else if (meetOrSlice == 'slice') ctx.scale(scaleMax, scaleMax);
  805. // translate
  806. ctx.translate(minX == null ? 0 : -minX, minY == null ? 0 : -minY);
  807. }
  808. // elements
  809. svg.Element = {}
  810. svg.Element.ElementBase = function (node) {
  811. this.attributes = {};
  812. this.styles = {};
  813. this.children = [];
  814. // get or create attribute
  815. this.attribute = function (name, createIfNotExists) {
  816. var a = this.attributes[name];
  817. if (a != null) return a;
  818. a = new svg.Property(name, '');
  819. if (createIfNotExists == true) this.attributes[name] = a;
  820. return a;
  821. }
  822. // get or create style, crawls up node tree
  823. this.style = function (name, createIfNotExists) {
  824. var s = this.styles[name];
  825. if (s != null) return s;
  826. var a = this.attribute(name);
  827. if (a != null && a.hasValue()) {
  828. return a;
  829. }
  830. var p = this.parent;
  831. if (p != null) {
  832. var ps = p.style(name);
  833. if (ps != null && ps.hasValue()) {
  834. return ps;
  835. }
  836. }
  837. s = new svg.Property(name, '');
  838. if (createIfNotExists == true) this.styles[name] = s;
  839. return s;
  840. }
  841. // base render
  842. this.render = function (ctx) {
  843. // don't render display=none
  844. if (this.style('display').value == 'none') return;
  845. // don't render visibility=hidden
  846. if (this.attribute('visibility').value == 'hidden') return;
  847. ctx.save();
  848. this.setContext(ctx);
  849. // mask
  850. if (this.attribute('mask').hasValue()) {
  851. var mask = this.attribute('mask').Definition.getDefinition();
  852. if (mask != null) mask.apply(ctx, this);
  853. }
  854. else if (this.style('filter').hasValue()) {
  855. var filter = this.style('filter').Definition.getDefinition();
  856. if (filter != null) filter.apply(ctx, this);
  857. }
  858. else this.renderChildren(ctx);
  859. this.clearContext(ctx);
  860. ctx.restore();
  861. }
  862. // base set context
  863. this.setContext = function (ctx) {
  864. // OVERRIDE ME!
  865. }
  866. // base clear context
  867. this.clearContext = function (ctx) {
  868. // OVERRIDE ME!
  869. }
  870. // base render children
  871. this.renderChildren = function (ctx) {
  872. for (var i = 0; i < this.children.length; i++) {
  873. this.children[i].render(ctx);
  874. }
  875. }
  876. this.addChild = function (childNode, create) {
  877. var child = childNode;
  878. if (create) child = svg.CreateElement(childNode);
  879. child.parent = this;
  880. this.children.push(child);
  881. }
  882. if (node != null && node.nodeType == 1) { //ELEMENT_NODE
  883. // add children
  884. for (var i = 0; i < node.childNodes.length; i++) {
  885. var childNode = node.childNodes[i];
  886. if (childNode.nodeType == 1) this.addChild(childNode, true); //ELEMENT_NODE
  887. }
  888. // add attributes
  889. for (var i = 0; i < node.attributes.length; i++) {
  890. var attribute = node.attributes[i];
  891. this.attributes[attribute.nodeName] = new svg.Property(attribute.nodeName, attribute.nodeValue);
  892. }
  893. // add tag styles
  894. var styles = svg.Styles[node.nodeName];
  895. if (styles != null) {
  896. for (var name in styles) {
  897. this.styles[name] = styles[name];
  898. }
  899. }
  900. // add class styles
  901. if (this.attribute('class').hasValue()) {
  902. var classes = svg.compressSpaces(this.attribute('class').value).split(' ');
  903. for (var j = 0; j < classes.length; j++) {
  904. styles = svg.Styles['.' + classes[j]];
  905. if (styles != null) {
  906. for (var name in styles) {
  907. this.styles[name] = styles[name];
  908. }
  909. }
  910. styles = svg.Styles[node.nodeName + '.' + classes[j]];
  911. if (styles != null) {
  912. for (var name in styles) {
  913. this.styles[name] = styles[name];
  914. }
  915. }
  916. }
  917. }
  918. // add inline styles
  919. if (this.attribute('style').hasValue()) {
  920. var styles = this.attribute('style').value.split(';');
  921. for (var i = 0; i < styles.length; i++) {
  922. if (svg.trim(styles[i]) != '') {
  923. var style = styles[i].split(':');
  924. var name = svg.trim(style[0]);
  925. var value = svg.trim(style[1]);
  926. this.styles[name] = new svg.Property(name, value);
  927. }
  928. }
  929. }
  930. // add id
  931. if (this.attribute('id').hasValue()) {
  932. if (svg.Definitions[this.attribute('id').value] == null) {
  933. svg.Definitions[this.attribute('id').value] = this;
  934. }
  935. }
  936. }
  937. }
  938. svg.Element.RenderedElementBase = function (node) {
  939. this.base = svg.Element.ElementBase;
  940. this.base(node);
  941. this.setContext = function (ctx) {
  942. // fill
  943. if (this.style('fill').Definition.isUrl()) {
  944. var fs = this.style('fill').Definition.getFillStyle(this);
  945. if (fs != null) ctx.fillStyle = fs;
  946. }
  947. else if (this.style('fill').hasValue()) {
  948. var fillStyle = this.style('fill');
  949. if (this.style('fill-opacity').hasValue()) fillStyle = fillStyle.Color.addOpacity(this.style('fill-opacity').value);
  950. ctx.fillStyle = (fillStyle.value == 'none' ? 'rgba(0,0,0,0)' : fillStyle.value);
  951. }
  952. // stroke
  953. if (this.style('stroke').Definition.isUrl()) {
  954. var fs = this.style('stroke').Definition.getFillStyle(this);
  955. if (fs != null) ctx.strokeStyle = fs;
  956. }
  957. else if (this.style('stroke').hasValue()) {
  958. var strokeStyle = this.style('stroke');
  959. if (this.style('stroke-opacity').hasValue()) strokeStyle = strokeStyle.Color.addOpacity(this.style('stroke-opacity').value);
  960. ctx.strokeStyle = (strokeStyle.value == 'none' ? 'rgba(0,0,0,0)' : strokeStyle.value);
  961. }
  962. if (this.style('stroke-width').hasValue()) ctx.lineWidth = this.style('stroke-width').Length.toPixels();
  963. if (this.style('stroke-linecap').hasValue()) ctx.lineCap = this.style('stroke-linecap').value;
  964. if (this.style('stroke-linejoin').hasValue()) ctx.lineJoin = this.style('stroke-linejoin').value;
  965. if (this.style('stroke-miterlimit').hasValue()) ctx.miterLimit = this.style('stroke-miterlimit').value;
  966. // font
  967. if (typeof (ctx.font) != 'undefined') {
  968. ctx.font = svg.Font.CreateFont(
  969. this.style('font-style').value,
  970. this.style('font-variant').value,
  971. this.style('font-weight').value,
  972. this.style('font-size').hasValue() ? this.style('font-size').Length.toPixels() + 'px' : '',
  973. this.style('font-family').value).toString();
  974. }
  975. // transform
  976. if (this.attribute('transform').hasValue()) {
  977. var transform = new svg.Transform(this.attribute('transform').value);
  978. transform.apply(ctx);
  979. }
  980. // clip
  981. if (this.attribute('clip-path').hasValue()) {
  982. var clip = this.attribute('clip-path').Definition.getDefinition();
  983. if (clip != null) clip.apply(ctx);
  984. }
  985. // opacity
  986. if (this.style('opacity').hasValue()) {
  987. ctx.globalAlpha = this.style('opacity').numValue();
  988. }
  989. }
  990. }
  991. svg.Element.RenderedElementBase.prototype = new svg.Element.ElementBase;
  992. svg.Element.PathElementBase = function (node) {
  993. this.base = svg.Element.RenderedElementBase;
  994. this.base(node);
  995. this.path = function (ctx) {
  996. if (ctx != null) ctx.beginPath();
  997. return new svg.BoundingBox();
  998. }
  999. this.renderChildren = function (ctx) {
  1000. this.path(ctx);
  1001. svg.Mouse.checkPath(this, ctx);
  1002. if (ctx.fillStyle != '') ctx.fill();
  1003. if (ctx.strokeStyle != '') ctx.stroke();
  1004. var markers = this.getMarkers();
  1005. if (markers != null) {
  1006. if (this.style('marker-start').Definition.isUrl()) {
  1007. var marker = this.style('marker-start').Definition.getDefinition();
  1008. marker.render(ctx, markers[0][0], markers[0][1]);
  1009. }
  1010. if (this.style('marker-mid').Definition.isUrl()) {
  1011. var marker = this.style('marker-mid').Definition.getDefinition();
  1012. for (var i = 1; i < markers.length - 1; i++) {
  1013. marker.render(ctx, markers[i][0], markers[i][1]);
  1014. }
  1015. }
  1016. if (this.style('marker-end').Definition.isUrl()) {
  1017. var marker = this.style('marker-end').Definition.getDefinition();
  1018. marker.render(ctx, markers[markers.length - 1][0], markers[markers.length - 1][1]);
  1019. }
  1020. }
  1021. }
  1022. this.getBoundingBox = function () {
  1023. return this.path();
  1024. }
  1025. this.getMarkers = function () {
  1026. return null;
  1027. }
  1028. }
  1029. svg.Element.PathElementBase.prototype = new svg.Element.RenderedElementBase;
  1030. // svg element
  1031. svg.Element.svg = function (node) {
  1032. this.base = svg.Element.RenderedElementBase;
  1033. this.base(node);
  1034. this.baseClearContext = this.clearContext;
  1035. this.clearContext = function (ctx) {
  1036. this.baseClearContext(ctx);
  1037. svg.ViewPort.RemoveCurrent();
  1038. }
  1039. this.baseSetContext = this.setContext;
  1040. this.setContext = function (ctx) {
  1041. // initial values
  1042. ctx.strokeStyle = 'rgba(0,0,0,0)';
  1043. ctx.lineCap = 'butt';
  1044. ctx.lineJoin = 'miter';
  1045. ctx.miterLimit = 4;
  1046. this.baseSetContext(ctx);
  1047. // create new view port
  1048. if (this.attribute('x').hasValue() && this.attribute('y').hasValue()) {
  1049. ctx.translate(this.attribute('x').Length.toPixels('x'), this.attribute('y').Length.toPixels('y'));
  1050. }
  1051. var width = svg.ViewPort.width();
  1052. var height = svg.ViewPort.height();
  1053. if (typeof (this.root) == 'undefined' && this.attribute('width').hasValue() && this.attribute('height').hasValue()) {
  1054. width = this.attribute('width').Length.toPixels('x');
  1055. height = this.attribute('height').Length.toPixels('y');
  1056. var x = 0;
  1057. var y = 0;
  1058. if (this.attribute('refX').hasValue() && this.attribute('refY').hasValue()) {
  1059. x = -this.attribute('refX').Length.toPixels('x');
  1060. y = -this.attribute('refY').Length.toPixels('y');
  1061. }
  1062. ctx.beginPath();
  1063. ctx.moveTo(x, y);
  1064. ctx.lineTo(width, y);
  1065. ctx.lineTo(width, height);
  1066. ctx.lineTo(x, height);
  1067. ctx.closePath();
  1068. ctx.clip();
  1069. }
  1070. svg.ViewPort.SetCurrent(width, height);
  1071. // viewbox
  1072. if (this.attribute('viewBox').hasValue()) {
  1073. var viewBox = svg.ToNumberArray(this.attribute('viewBox').value);
  1074. var minX = viewBox[0];
  1075. var minY = viewBox[1];
  1076. width = viewBox[2];
  1077. height = viewBox[3];
  1078. svg.AspectRatio(ctx,
  1079. this.attribute('preserveAspectRatio').value,
  1080. svg.ViewPort.width(),
  1081. width,
  1082. svg.ViewPort.height(),
  1083. height,
  1084. minX,
  1085. minY,
  1086. this.attribute('refX').value,
  1087. this.attribute('refY').value);
  1088. svg.ViewPort.RemoveCurrent();
  1089. svg.ViewPort.SetCurrent(viewBox[2], viewBox[3]);
  1090. }
  1091. }
  1092. }
  1093. svg.Element.svg.prototype = new svg.Element.RenderedElementBase;
  1094. // rect element
  1095. svg.Element.rect = function (node) {
  1096. this.base = svg.Element.PathElementBase;
  1097. this.base(node);
  1098. this.path = function (ctx) {
  1099. var x = this.attribute('x').Length.toPixels('x');
  1100. var y = this.attribute('y').Length.toPixels('y');
  1101. var width = this.attribute('width').Length.toPixels('x');
  1102. var height = this.attribute('height').Length.toPixels('y');
  1103. var rx = this.attribute('rx').Length.toPixels('x');
  1104. var ry = this.attribute('ry').Length.toPixels('y');
  1105. if (this.attribute('rx').hasValue() && !this.attribute('ry').hasValue()) ry = rx;
  1106. if (this.attribute('ry').hasValue() && !this.attribute('rx').hasValue()) rx = ry;
  1107. if (ctx != null) {
  1108. ctx.beginPath();
  1109. ctx.moveTo(x + rx, y);
  1110. ctx.lineTo(x + width - rx, y);
  1111. ctx.quadraticCurveTo(x + width, y, x + width, y + ry)
  1112. ctx.lineTo(x + width, y + height - ry);
  1113. ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height)
  1114. ctx.lineTo(x + rx, y + height);
  1115. ctx.quadraticCurveTo(x, y + height, x, y + height - ry)
  1116. ctx.lineTo(x, y + ry);
  1117. ctx.quadraticCurveTo(x, y, x + rx, y)
  1118. ctx.closePath();
  1119. }
  1120. return new svg.BoundingBox(x, y, x + width, y + height);
  1121. }
  1122. }
  1123. svg.Element.rect.prototype = new svg.Element.PathElementBase;
  1124. // circle element
  1125. svg.Element.circle = function (node) {
  1126. this.base = svg.Element.PathElementBase;
  1127. this.base(node);
  1128. this.path = function (ctx) {
  1129. var cx = this.attribute('cx').Length.toPixels('x');
  1130. var cy = this.attribute('cy').Length.toPixels('y');
  1131. var r = this.attribute('r').Length.toPixels();
  1132. if (ctx != null) {
  1133. ctx.beginPath();
  1134. ctx.arc(cx, cy, r, 0, Math.PI * 2, true);
  1135. ctx.closePath();
  1136. }
  1137. return new svg.BoundingBox(cx - r, cy - r, cx + r, cy + r);
  1138. }
  1139. }
  1140. svg.Element.circle.prototype = new svg.Element.PathElementBase;
  1141. // ellipse element
  1142. svg.Element.ellipse = function (node) {
  1143. this.base = svg.Element.PathElementBase;
  1144. this.base(node);
  1145. this.path = function (ctx) {
  1146. var KAPPA = 4 * ((Math.sqrt(2) - 1) / 3);
  1147. var rx = this.attribute('rx').Length.toPixels('x');
  1148. var ry = this.attribute('ry').Length.toPixels('y');
  1149. var cx = this.attribute('cx').Length.toPixels('x');
  1150. var cy = this.attribute('cy').Length.toPixels('y');
  1151. if (ctx != null) {
  1152. ctx.beginPath();
  1153. ctx.moveTo(cx, cy - ry);
  1154. ctx.bezierCurveTo(cx + (KAPPA * rx), cy - ry, cx + rx, cy - (KAPPA * ry), cx + rx, cy);
  1155. ctx.bezierCurveTo(cx + rx, cy + (KAPPA * ry), cx + (KAPPA * rx), cy + ry, cx, cy + ry);
  1156. ctx.bezierCurveTo(cx - (KAPPA * rx), cy + ry, cx - rx, cy + (KAPPA * ry), cx - rx, cy);
  1157. ctx.bezierCurveTo(cx - rx, cy - (KAPPA * ry), cx - (KAPPA * rx), cy - ry, cx, cy - ry);
  1158. ctx.closePath();
  1159. }
  1160. return new svg.BoundingBox(cx - rx, cy - ry, cx + rx, cy + ry);
  1161. }
  1162. }
  1163. svg.Element.ellipse.prototype = new svg.Element.PathElementBase;
  1164. // line element
  1165. svg.Element.line = function (node) {
  1166. this.base = svg.Element.PathElementBase;
  1167. this.base(node);
  1168. this.getPoints = function () {
  1169. return [
  1170. new svg.Point(this.attribute('x1').Length.toPixels('x'), this.attribute('y1').Length.toPixels('y')),
  1171. new svg.Point(this.attribute('x2').Length.toPixels('x'), this.attribute('y2').Length.toPixels('y'))];
  1172. }
  1173. this.path = function (ctx) {
  1174. var points = this.getPoints();
  1175. if (ctx != null) {
  1176. ctx.beginPath();
  1177. ctx.moveTo(points[0].x, points[0].y);
  1178. ctx.lineTo(points[1].x, points[1].y);
  1179. }
  1180. return new svg.BoundingBox(points[0].x, points[0].y, points[1].x, points[1].y);
  1181. }
  1182. this.getMarkers = function () {
  1183. var points = this.getPoints();
  1184. var a = points[0].angleTo(points[1]);
  1185. return [[points[0], a], [points[1], a]];
  1186. }
  1187. }
  1188. svg.Element.line.prototype = new svg.Element.PathElementBase;
  1189. // polyline element
  1190. svg.Element.polyline = function (node) {
  1191. this.base = svg.Element.PathElementBase;
  1192. this.base(node);
  1193. this.points = svg.CreatePath(this.attribute('points').value);
  1194. this.path = function (ctx) {
  1195. var bb = new svg.BoundingBox(this.points[0].x, this.points[0].y);
  1196. if (ctx != null) {
  1197. ctx.beginPath();
  1198. ctx.moveTo(this.points[0].x, this.points[0].y);
  1199. }
  1200. for (var i = 1; i < this.points.length; i++) {
  1201. bb.addPoint(this.points[i].x, this.points[i].y);
  1202. if (ctx != null) ctx.lineTo(this.points[i].x, this.points[i].y);
  1203. }
  1204. return bb;
  1205. }
  1206. this.getMarkers = function () {
  1207. var markers = [];
  1208. for (var i = 0; i < this.points.length - 1; i++) {
  1209. markers.push([this.points[i], this.points[i].angleTo(this.points[i + 1])]);
  1210. }
  1211. markers.push([this.points[this.points.length - 1], markers[markers.length - 1][1]]);
  1212. return markers;
  1213. }
  1214. }
  1215. svg.Element.polyline.prototype = new svg.Element.PathElementBase;
  1216. // polygon element
  1217. svg.Element.polygon = function (node) {
  1218. this.base = svg.Element.polyline;
  1219. this.base(node);
  1220. this.basePath = this.path;
  1221. this.path = function (ctx) {
  1222. var bb = this.basePath(ctx);
  1223. if (ctx != null) {
  1224. ctx.lineTo(this.points[0].x, this.points[0].y);
  1225. ctx.closePath();
  1226. }
  1227. return bb;
  1228. }
  1229. }
  1230. svg.Element.polygon.prototype = new svg.Element.polyline;
  1231. // path element
  1232. svg.Element.path = function (node) {
  1233. this.base = svg.Element.PathElementBase;
  1234. this.base(node);
  1235. var d = this.attribute('d').value;
  1236. // TODO: convert to real lexer based on http://www.w3.org/TR/SVG11/paths.html#PathDataBNF
  1237. d = d.replace(/,/gm, ' '); // get rid of all commas
  1238. d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm, '$1 $2'); // separate commands from commands
  1239. d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm, '$1 $2'); // separate commands from commands
  1240. d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([^\s])/gm, '$1 $2'); // separate commands from points
  1241. d = d.replace(/([^\s])([MmZzLlHhVvCcSsQqTtAa])/gm, '$1 $2'); // separate commands from points
  1242. d = d.replace(/([0-9])([+\-])/gm, '$1 $2'); // separate digits when no comma
  1243. d = d.replace(/(\.[0-9]*)(\.)/gm, '$1 $2'); // separate digits when no comma
  1244. d = d.replace(/([Aa](\s+[0-9]+){3})\s+([01])\s*([01])/gm, '$1 $3 $4 '); // shorthand elliptical arc path syntax
  1245. d = svg.compressSpaces(d); // compress multiple spaces
  1246. d = svg.trim(d);
  1247. this.PathParser = new (function (d) {
  1248. this.tokens = d.split(' ');
  1249. this.reset = function () {
  1250. this.i = -1;
  1251. this.command = '';
  1252. this.previousCommand = '';
  1253. this.start = new svg.Point(0, 0);
  1254. this.control = new svg.Point(0, 0);
  1255. this.current = new svg.Point(0, 0);
  1256. this.points = [];
  1257. this.angles = [];
  1258. }
  1259. this.isEnd = function () {
  1260. return this.i >= this.tokens.length - 1;
  1261. }
  1262. this.isCommandOrEnd = function () {
  1263. if (this.isEnd()) return true;
  1264. return this.tokens[this.i + 1].match(/^[A-Za-z]$/) != null;
  1265. }
  1266. this.isRelativeCommand = function () {
  1267. return this.command == this.command.toLowerCase();
  1268. }
  1269. this.getToken = function () {
  1270. this.i = this.i + 1;
  1271. return this.tokens[this.i];
  1272. }
  1273. this.getScalar = function () {
  1274. return parseFloat(this.getToken());
  1275. }
  1276. this.nextCommand = function () {
  1277. this.previousCommand = this.command;
  1278. this.command = this.getToken();
  1279. }
  1280. this.getPoint = function () {
  1281. var p = new svg.Point(this.getScalar(), this.getScalar());
  1282. return this.makeAbsolute(p);
  1283. }
  1284. this.getAsControlPoint = function () {
  1285. var p = this.getPoint();
  1286. this.control = p;
  1287. return p;
  1288. }
  1289. this.getAsCurrentPoint = function () {
  1290. var p = this.getPoint();
  1291. this.current = p;
  1292. return p;
  1293. }
  1294. this.getReflectedControlPoint = function () {
  1295. if (this.previousCommand.toLowerCase() != 'c' && this.previousCommand.toLowerCase() != 's') {
  1296. return this.current;
  1297. }
  1298. // reflect point
  1299. var p = new svg.Point(2 * this.current.x - this.control.x, 2 * this.current.y - this.control.y);
  1300. return p;
  1301. }
  1302. this.makeAbsolute = function (p) {
  1303. if (this.isRelativeCommand()) {
  1304. p.x = this.current.x + p.x;
  1305. p.y = this.current.y + p.y;
  1306. }
  1307. return p;
  1308. }
  1309. this.addMarker = function (p, from, priorTo) {
  1310. // if the last angle isn't filled in because we didn't have this point yet ...
  1311. if (priorTo != null && this.angles.length > 0 && this.angles[this.angles.length - 1] == null) {
  1312. this.angles[this.angles.length - 1] = this.points[this.points.length - 1].angleTo(priorTo);
  1313. }
  1314. this.addMarkerAngle(p, from == null ? null : from.angleTo(p));
  1315. }
  1316. this.addMarkerAngle = function (p, a) {
  1317. this.points.push(p);
  1318. this.angles.push(a);
  1319. }
  1320. this.getMarkerPoints = function () { return this.points; }
  1321. this.getMarkerAngles = function () {
  1322. for (var i = 0; i < this.angles.length; i++) {
  1323. if (this.angles[i] == null) {
  1324. for (var j = i + 1; j < this.angles.length; j++) {
  1325. if (this.angles[j] != null) {
  1326. this.angles[i] = this.angles[j];
  1327. break;
  1328. }
  1329. }
  1330. }
  1331. }
  1332. return this.angles;
  1333. }
  1334. })(d);
  1335. this.path = function (ctx) {
  1336. var pp = this.PathParser;
  1337. pp.reset();
  1338. var bb = new svg.BoundingBox();
  1339. if (ctx != null) ctx.beginPath();
  1340. while (!pp.isEnd()) {
  1341. pp.nextCommand();
  1342. switch (pp.command.toUpperCase()) {
  1343. case 'M':
  1344. var p = pp.getAsCurrentPoint();
  1345. pp.addMarker(p);
  1346. bb.addPoint(p.x, p.y);
  1347. if (ctx != null) ctx.moveTo(p.x, p.y);
  1348. pp.start = pp.current;
  1349. while (!pp.isCommandOrEnd()) {
  1350. var p = pp.getAsCurrentPoint();
  1351. pp.addMarker(p, pp.start);
  1352. bb.addPoint(p.x, p.y);
  1353. if (ctx != null) ctx.lineTo(p.x, p.y);
  1354. }
  1355. break;
  1356. case 'L':
  1357. while (!pp.isCommandOrEnd()) {
  1358. var c = pp.current;
  1359. var p = pp.getAsCurrentPoint();
  1360. pp.addMarker(p, c);
  1361. bb.addPoint(p.x, p.y);
  1362. if (ctx != null) ctx.lineTo(p.x, p.y);
  1363. }
  1364. break;
  1365. case 'H':
  1366. while (!pp.isCommandOrEnd()) {
  1367. var newP = new svg.Point((pp.isRelativeCommand() ? pp.current.x : 0) + pp.getScalar(), pp.current.y);
  1368. pp.addMarker(newP, pp.current);
  1369. pp.current = newP;
  1370. bb.addPoint(pp.current.x, pp.current.y);
  1371. if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y);
  1372. }
  1373. break;
  1374. case 'V':
  1375. while (!pp.isCommandOrEnd()) {
  1376. var newP = new svg.Point(pp.current.x, (pp.isRelativeCommand() ? pp.current.y : 0) + pp.getScalar());
  1377. pp.addMarker(newP, pp.current);
  1378. pp.current = newP;
  1379. bb.addPoint(pp.current.x, pp.current.y);
  1380. if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y);
  1381. }
  1382. break;
  1383. case 'C':
  1384. while (!pp.isCommandOrEnd()) {
  1385. var curr = pp.current;
  1386. var p1 = pp.getPoint();
  1387. var cntrl = pp.getAsControlPoint();
  1388. var cp = pp.getAsCurrentPoint();
  1389. pp.addMarker(cp, cntrl, p1);
  1390. bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
  1391. if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
  1392. }
  1393. break;
  1394. case 'S':
  1395. while (!pp.isCommandOrEnd()) {
  1396. var curr = pp.current;
  1397. var p1 = pp.getReflectedControlPoint();
  1398. var cntrl = pp.getAsControlPoint();
  1399. var cp = pp.getAsCurrentPoint();
  1400. pp.addMarker(cp, cntrl, p1);
  1401. bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
  1402. if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
  1403. }
  1404. break;
  1405. case 'Q':
  1406. while (!pp.isCommandOrEnd()) {
  1407. var curr = pp.current;
  1408. var cntrl = pp.getAsControlPoint();
  1409. var cp = pp.getAsCurrentPoint();
  1410. pp.addMarker(cp, cntrl, cntrl);
  1411. bb.addQuadraticCurve(curr.x, curr.y, cntrl.x, cntrl.y, cp.x, cp.y);
  1412. if (ctx != null) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y);
  1413. }
  1414. break;
  1415. case 'T':
  1416. while (!pp.isCommandOrEnd()) {
  1417. var curr = pp.current;
  1418. var cntrl = pp.getReflectedControlPoint();
  1419. pp.control = cntrl;
  1420. var cp = pp.getAsCurrentPoint();
  1421. pp.addMarker(cp, cntrl, cntrl);
  1422. bb.addQuadraticCurve(curr.x, curr.y, cntrl.x, cntrl.y, cp.x, cp.y);
  1423. if (ctx != null) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y);
  1424. }
  1425. break;
  1426. case 'A':
  1427. while (!pp.isCommandOrEnd()) {
  1428. var curr = pp.current;
  1429. var rx = pp.getScalar();
  1430. var ry = pp.getScalar();
  1431. var xAxisRotation = pp.getScalar() * (Math.PI / 180.0);
  1432. var largeArcFlag = pp.getScalar();
  1433. var sweepFlag = pp.getScalar();
  1434. var cp = pp.getAsCurrentPoint();
  1435. // Conversion from endpoint to center parameterization
  1436. // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
  1437. // x1', y1'
  1438. var currp = new svg.Point(
  1439. Math.cos(xAxisRotation) * (curr.x - cp.x) / 2.0 + Math.sin(xAxisRotation) * (curr.y - cp.y) / 2.0,
  1440. -Math.sin(xAxisRotation) * (curr.x - cp.x) / 2.0 + Math.cos(xAxisRotation) * (curr.y - cp.y) / 2.0
  1441. );
  1442. // adjust radii
  1443. var l = Math.pow(currp.x, 2) / Math.pow(rx, 2) + Math.pow(currp.y, 2) / Math.pow(ry, 2);
  1444. if (l > 1) {
  1445. rx *= Math.sqrt(l);
  1446. ry *= Math.sqrt(l);
  1447. }
  1448. // cx', cy'
  1449. var s = (largeArcFlag == sweepFlag ? -1 : 1) * Math.sqrt(
  1450. ((Math.pow(rx, 2) * Math.pow(ry, 2)) - (Math.pow(rx, 2) * Math.pow(currp.y, 2)) - (Math.pow(ry, 2) * Math.pow(currp.x, 2))) /
  1451. (Math.pow(rx, 2) * Math.pow(currp.y, 2) + Math.pow(ry, 2) * Math.pow(currp.x, 2))
  1452. );
  1453. if (isNaN(s)) s = 0;
  1454. var cpp = new svg.Point(s * rx * currp.y / ry, s * -ry * currp.x / rx);
  1455. // cx, cy
  1456. var centp = new svg.Point(
  1457. (curr.x + cp.x) / 2.0 + Math.cos(xAxisRotation) * cpp.x - Math.sin(xAxisRotation) * cpp.y,
  1458. (curr.y + cp.y) / 2.0 + Math.sin(xAxisRotation) * cpp.x + Math.cos(xAxisRotation) * cpp.y
  1459. );
  1460. // vector magnitude
  1461. var m = function (v) { return Math.sqrt(Math.pow(v[0], 2) + Math.pow(v[1], 2)); }
  1462. // ratio between two vectors
  1463. var r = function (u, v) { return (u[0] * v[0] + u[1] * v[1]) / (m(u) * m(v)) }
  1464. // angle between two vectors
  1465. var a = function (u, v) { return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) * Math.acos(r(u, v)); }
  1466. // initial angle
  1467. var a1 = a([1, 0], [(currp.x - cpp.x) / rx, (currp.y - cpp.y) / ry]);
  1468. // angle delta
  1469. var u = [(currp.x - cpp.x) / rx, (currp.y - cpp.y) / ry];
  1470. var v = [(-currp.x - cpp.x) / rx, (-currp.y - cpp.y) / ry];
  1471. var ad = a(u, v);
  1472. if (r(u, v) <= -1) ad = Math.PI;
  1473. if (r(u, v) >= 1) ad = 0;
  1474. if (sweepFlag == 0 && ad > 0) ad = ad - 2 * Math.PI;
  1475. if (sweepFlag == 1 && ad < 0) ad = ad + 2 * Math.PI;
  1476. // for markers
  1477. var halfWay = new svg.Point(
  1478. centp.x - rx * Math.cos((a1 + ad) / 2),
  1479. centp.y - ry * Math.sin((a1 + ad) / 2)
  1480. );
  1481. pp.addMarkerAngle(halfWay, (a1 + ad) / 2 + (sweepFlag == 0 ? 1 : -1) * Math.PI / 2);
  1482. pp.addMarkerAngle(cp, ad + (sweepFlag == 0 ? 1 : -1) * Math.PI / 2);
  1483. bb.addPoint(cp.x, cp.y); // TODO: this is too naive, make it better
  1484. if (ctx != null) {
  1485. var r = rx > ry ? rx : ry;
  1486. var sx = rx > ry ? 1 : rx / ry;
  1487. var sy = rx > ry ? ry / rx : 1;
  1488. ctx.translate(centp.x, centp.y);
  1489. ctx.rotate(xAxisRotation);
  1490. ctx.scale(sx, sy);
  1491. ctx.arc(0, 0, r, a1, a1 + ad, 1 - sweepFlag);
  1492. ctx.scale(1 / sx, 1 / sy);
  1493. ctx.rotate(-xAxisRotation);
  1494. ctx.translate(-centp.x, -centp.y);
  1495. }
  1496. }
  1497. break;
  1498. case 'Z':
  1499. if (ctx != null) ctx.closePath();
  1500. pp.current = pp.start;
  1501. }
  1502. }
  1503. return bb;
  1504. }
  1505. this.getMarkers = function () {
  1506. var points = this.PathParser.getMarkerPoints();
  1507. var angles = this.PathParser.getMarkerAngles();
  1508. var markers = [];
  1509. for (var i = 0; i < points.length; i++) {
  1510. markers.push([points[i], angles[i]]);
  1511. }
  1512. return markers;
  1513. }
  1514. }
  1515. svg.Element.path.prototype = new svg.Element.PathElementBase;
  1516. // pattern element
  1517. svg.Element.pattern = function (node) {
  1518. this.base = svg.Element.ElementBase;
  1519. this.base(node);
  1520. this.createPattern = function (ctx, element) {
  1521. // render me using a temporary svg element
  1522. var tempSvg = new svg.Element.svg();
  1523. tempSvg.attributes['viewBox'] = new svg.Property('viewBox', this.attribute('viewBox').value);
  1524. tempSvg.attributes['x'] = new svg.Property('x', this.attribute('x').value);
  1525. tempSvg.attributes['y'] = new svg.Property('y', this.attribute('y').value);
  1526. tempSvg.attributes['width'] = new svg.Property('width', this.attribute('width').value);
  1527. tempSvg.attributes['height'] = new svg.Property('height', this.attribute('height').value);
  1528. tempSvg.children = this.children;
  1529. var c = document.createElement('canvas');
  1530. c.width = this.attribute('width').Length.toPixels('x');
  1531. c.height = this.attribute('height').Length.toPixels('y');
  1532. tempSvg.render(c.getContext('2d'));
  1533. return ctx.createPattern(c, 'repeat');
  1534. }
  1535. }
  1536. svg.Element.pattern.prototype = new svg.Element.ElementBase;
  1537. // marker element
  1538. svg.Element.marker = function (node) {
  1539. this.base = svg.Element.ElementBase;
  1540. this.base(node);
  1541. this.baseRender = this.render;
  1542. this.render = function (ctx, point, angle) {
  1543. ctx.translate(point.x, point.y);
  1544. if (this.attribute('orient').valueOrDefault('auto') == 'auto') ctx.rotate(angle);
  1545. if (this.attribute('markerUnits').valueOrDefault('strokeWidth') == 'strokeWidth') ctx.scale(ctx.lineWidth, ctx.lineWidth);
  1546. ctx.save();
  1547. // render me using a temporary svg element
  1548. var tempSvg = new svg.Element.svg();
  1549. tempSvg.attributes['viewBox'] = new svg.Property('viewBox', this.attribute('viewBox').value);
  1550. tempSvg.attributes['refX'] = new svg.Property('refX', this.attribute('refX').value);
  1551. tempSvg.attributes['refY'] = new svg.Property('refY', this.attribute('refY').value);
  1552. tempSvg.attributes['width'] = new svg.Property('width', this.attribute('markerWidth').value);
  1553. tempSvg.attributes['height'] = new svg.Property('height', this.attribute('markerHeight').value);
  1554. tempSvg.attributes['fill'] = new svg.Property('fill', this.attribute('fill').valueOrDefault('black'));
  1555. tempSvg.attributes['stroke'] = new svg.Property('stroke', this.attribute('stroke').valueOrDefault('none'));
  1556. tempSvg.children = this.children;
  1557. tempSvg.render(ctx);
  1558. ctx.restore();
  1559. if (this.attribute('markerUnits').valueOrDefault('strokeWidth') == 'strokeWidth') ctx.scale(1 / ctx.lineWidth, 1 / ctx.lineWidth);
  1560. if (this.attribute('orient').valueOrDefault('auto') == 'auto') ctx.rotate(-angle);
  1561. ctx.translate(-point.x, -point.y);
  1562. }
  1563. }
  1564. svg.Element.marker.prototype = new svg.Element.ElementBase;
  1565. // definitions element
  1566. svg.Element.defs = function (node) {
  1567. this.base = svg.Element.ElementBase;
  1568. this.base(node);
  1569. this.render = function (ctx) {
  1570. // NOOP
  1571. }
  1572. }
  1573. svg.Element.defs.prototype = new svg.Element.ElementBase;
  1574. // base for gradients
  1575. svg.Element.GradientBase = function (node) {
  1576. this.base = svg.Element.ElementBase;
  1577. this.base(node);
  1578. this.gradientUnits = this.attribute('gradientUnits').valueOrDefault('objectBoundingBox');
  1579. this.stops = [];
  1580. for (var i = 0; i < this.children.length; i++) {
  1581. var child = this.children[i];
  1582. this.stops.push(child);
  1583. }
  1584. this.getGradient = function () {
  1585. // OVERRIDE ME!
  1586. }
  1587. this.createGradient = function (ctx, element) {
  1588. var stopsContainer = this;
  1589. if (this.attribute('xlink:href').hasValue()) {
  1590. stopsContainer = this.attribute('xlink:href').Definition.getDefinition();
  1591. }
  1592. var g = this.getGradient(ctx, element);
  1593. for (var i = 0; i < stopsContainer.stops.length; i++) {
  1594. g.addColorStop(stopsContainer.stops[i].offset, stopsContainer.stops[i].color);
  1595. }
  1596. if (this.attribute('gradientTransform').hasValue()) {
  1597. // render as transformed pattern on temporary canvas
  1598. var rootView = svg.ViewPort.viewPorts[0];
  1599. var rect = new svg.Element.rect();
  1600. rect.attributes['x'] = new svg.Property('x', -svg.MAX_VIRTUAL_PIXELS / 3.0);
  1601. rect.attributes['y'] = new svg.Property('y', -svg.MAX_VIRTUAL_PIXELS / 3.0);
  1602. rect.attributes['width'] = new svg.Property('width', svg.MAX_VIRTUAL_PIXELS);
  1603. rect.attributes['height'] = new svg.Property('height', svg.MAX_VIRTUAL_PIXELS);
  1604. var group = new svg.Element.g();
  1605. group.attributes['transform'] = new svg.Property('transform', this.attribute('gradientTransform').value);
  1606. group.children = [rect];
  1607. var tempSvg = new svg.Element.svg();
  1608. tempSvg.attributes['x'] = new svg.Property('x', 0);
  1609. tempSvg.attributes['y'] = new svg.Property('y', 0);
  1610. tempSvg.attributes['width'] = new svg.Property('width', rootView.width);
  1611. tempSvg.attributes['height'] = new svg.Property('height', rootView.height);
  1612. tempSvg.children = [group];
  1613. var c = document.createElement('canvas');
  1614. c.width = rootView.width;
  1615. c.height = rootView.height;
  1616. var tempCtx = c.getContext('2d');
  1617. tempCtx.fillStyle = g;
  1618. tempSvg.render(tempCtx);
  1619. return tempCtx.createPattern(c, 'no-repeat');
  1620. }
  1621. return g;
  1622. }
  1623. }
  1624. svg.Element.GradientBase.prototype = new svg.Element.ElementBase;
  1625. // linear gradient element
  1626. svg.Element.linearGradient = function (node) {
  1627. this.base = svg.Element.GradientBase;
  1628. this.base(node);
  1629. this.getGradient = function (ctx, element) {
  1630. var bb = element.getBoundingBox();
  1631. var x1 = (this.gradientUnits == 'objectBoundingBox'
  1632. ? bb.x() + bb.width() * this.attribute('x1').numValue()
  1633. : this.attribute('x1').Length.toPixels('x'));
  1634. var y1 = (this.gradientUnits == 'objectBoundingBox'
  1635. ? bb.y() + bb.height() * this.attribute('y1').numValue()
  1636. : this.attribute('y1').Length.toPixels('y'));
  1637. var x2 = (this.gradientUnits == 'objectBoundingBox'
  1638. ? bb.x() + bb.width() * this.attribute('x2').numValue()
  1639. : this.attribute('x2').Length.toPixels('x'));
  1640. var y2 = (this.gradientUnits == 'objectBoundingBox'
  1641. ? bb.y() + bb.height() * this.attribute('y2').numValue()
  1642. : this.attribute('y2').Length.toPixels('y'));
  1643. return ctx.createLinearGradient(x1, y1, x2, y2);
  1644. }
  1645. }
  1646. svg.Element.linearGradient.prototype = new svg.Element.GradientBase;
  1647. // radial gradient element
  1648. svg.Element.radialGradient = function (node) {
  1649. this.base = svg.Element.GradientBase;
  1650. this.base(node);
  1651. this.getGradient = function (ctx, element) {
  1652. var bb = element.getBoundingBox();
  1653. var cx = (this.gradientUnits == 'objectBoundingBox'
  1654. ? bb.x() + bb.width() * this.attribute('cx').numValue()
  1655. : this.attribute('cx').Length.toPixels('x'));
  1656. var cy = (this.gradientUnits == 'objectBoundingBox'
  1657. ? bb.y() + bb.height() * this.attribute('cy').numValue()
  1658. : this.attribute('cy').Length.toPixels('y'));
  1659. var fx = cx;
  1660. var fy = cy;
  1661. if (this.attribute('fx').hasValue()) {
  1662. fx = (this.gradientUnits == 'objectBoundingBox'
  1663. ? bb.x() + bb.width() * this.attribute('fx').numValue()
  1664. : this.attribute('fx').Length.toPixels('x'));
  1665. }
  1666. if (this.attribute('fy').hasValue()) {
  1667. fy = (this.gradientUnits == 'objectBoundingBox'
  1668. ? bb.y() + bb.height() * this.attribute('fy').numValue()
  1669. : this.attribute('fy').Length.toPixels('y'));
  1670. }
  1671. var r = (this.gradientUnits == 'objectBoundingBox'
  1672. ? (bb.width() + bb.height()) / 2.0 * this.attribute('r').numValue()
  1673. : this.attribute('r').Length.toPixels());
  1674. return ctx.createRadialGradient(fx, fy, 0, cx, cy, r);
  1675. }
  1676. }
  1677. svg.Element.radialGradient.prototype = new svg.Element.GradientBase;
  1678. // gradient stop element
  1679. svg.Element.stop = function (node) {
  1680. this.base = svg.Element.ElementBase;
  1681. this.base(node);
  1682. this.offset = this.attribute('offset').numValue();
  1683. var stopColor = this.style('stop-color');
  1684. if (this.style('stop-opacity').hasValue()) stopColor = stopColor.Color.addOpacity(this.style('stop-opacity').value);
  1685. this.color = stopColor.value;
  1686. }
  1687. svg.Element.stop.prototype = new svg.Element.ElementBase;
  1688. // animation base element
  1689. svg.Element.AnimateBase = function (node) {
  1690. this.base = svg.Element.ElementBase;
  1691. this.base(node);
  1692. svg.Animations.push(this);
  1693. this.duration = 0.0;
  1694. this.begin = this.attribute('begin').Time.toMilliseconds();
  1695. this.maxDuration = this.begin + this.attribute('dur').Time.toMilliseconds();
  1696. this.getProperty = function () {
  1697. var attributeType = this.attribute('attributeType').value;
  1698. var attributeName = this.attribute('attributeName').value;
  1699. if (attributeType == 'CSS') {
  1700. return this.parent.style(attributeName, true);
  1701. }
  1702. return this.parent.attribute(attributeName, true);
  1703. };
  1704. this.initialValue = null;
  1705. this.removed = false;
  1706. this.calcValue = function () {
  1707. // OVERRIDE ME!
  1708. return '';
  1709. }
  1710. this.update = function (delta) {
  1711. // set initial value
  1712. if (this.initialValue == null) {
  1713. this.initialValue = this.getProperty().value;
  1714. }
  1715. // if we're past the end time
  1716. if (this.duration > this.maxDuration) {
  1717. // loop for indefinitely repeating animations
  1718. if (this.attribute('repeatCount').value == 'indefinite') {
  1719. this.duration = 0.0
  1720. }
  1721. else if (this.attribute('fill').valueOrDefault('remove') == 'remove' && !this.removed) {
  1722. this.removed = true;
  1723. this.getProperty().value = this.initialValue;
  1724. return true;
  1725. }
  1726. else {
  1727. return false; // no updates made
  1728. }
  1729. }
  1730. this.duration = this.duration + delta;
  1731. // if we're past the begin time
  1732. var updated = false;
  1733. if (this.begin < this.duration) {
  1734. var newValue = this.calcValue(); // tween
  1735. if (this.attribute('type').hasValue()) {
  1736. // for transform, etc.
  1737. var type = this.attribute('type').value;
  1738. newValue = type + '(' + newValue + ')';
  1739. }
  1740. this.getProperty().value = newValue;
  1741. updated = true;
  1742. }
  1743. return updated;
  1744. }
  1745. // fraction of duration we've covered
  1746. this.progress = function () {
  1747. return ((this.duration - this.begin) / (this.maxDuration - this.begin));
  1748. }
  1749. }
  1750. svg.Element.AnimateBase.prototype = new svg.Element.ElementBase;
  1751. // animate element
  1752. svg.Element.animate = function (node) {
  1753. this.base = svg.Element.AnimateBase;
  1754. this.base(node);
  1755. this.calcValue = function () {
  1756. var from = this.attribute('from').numValue();
  1757. var to = this.attribute('to').numValue();
  1758. // tween value linearly
  1759. return from + (to - from) * this.progress();
  1760. };
  1761. }
  1762. svg.Element.animate.prototype = new svg.Element.AnimateBase;
  1763. // animate color element
  1764. svg.Element.animateColor = function (node) {
  1765. this.base = svg.Element.AnimateBase;
  1766. this.base(node);
  1767. this.calcValue = function () {
  1768. var from = new RGBColor(this.attribute('from').value);
  1769. var to = new RGBColor(this.attribute('to').value);
  1770. if (from.ok && to.ok) {
  1771. // tween color linearly
  1772. var r = from.r + (to.r - from.r) * this.progress();
  1773. var g = from.g + (to.g - from.g) * this.progress();
  1774. var b = from.b + (to.b - from.b) * this.progress();
  1775. return 'rgb(' + parseInt(r, 10) + ',' + parseInt(g, 10) + ',' + parseInt(b, 10) + ')';
  1776. }
  1777. return this.attribute('from').value;
  1778. };
  1779. }
  1780. svg.Element.animateColor.prototype = new svg.Element.AnimateBase;
  1781. // animate transform element
  1782. svg.Element.animateTransform = function (node) {
  1783. this.base = svg.Element.animate;
  1784. this.base(node);
  1785. }
  1786. svg.Element.animateTransform.prototype = new svg.Element.animate;
  1787. // font element
  1788. svg.Element.font = function (node) {
  1789. this.base = svg.Element.ElementBase;
  1790. this.base(node);
  1791. this.horizAdvX = this.attribute('horiz-adv-x').numValue();
  1792. this.isRTL = false;
  1793. this.isArabic = false;
  1794. this.fontFace = null;
  1795. this.missingGlyph = null;
  1796. this.glyphs = [];
  1797. for (var i = 0; i < this.children.length; i++) {
  1798. var child = this.children[i];
  1799. if (child.type == 'font-face') {
  1800. this.fontFace = child;
  1801. if (child.style('font-family').hasValue()) {
  1802. svg.Definitions[child.style('font-family').value] = this;
  1803. }
  1804. }
  1805. else if (child.type == 'missing-glyph') this.missingGlyph = child;
  1806. else if (child.type == 'glyph') {
  1807. if (child.arabicForm != '') {
  1808. this.isRTL = true;
  1809. this.isArabic = true;
  1810. if (typeof (this.glyphs[child.unicode]) == 'undefined') this.glyphs[child.unicode] = [];
  1811. this.glyphs[child.unicode][child.arabicForm] = child;
  1812. }
  1813. else {
  1814. this.glyphs[child.unicode] = child;
  1815. }
  1816. }
  1817. }
  1818. }
  1819. svg.Element.font.prototype = new svg.Element.ElementBase;
  1820. // font-face element
  1821. svg.Element.fontface = function (node) {
  1822. this.base = svg.Element.ElementBase;
  1823. this.base(node);
  1824. this.ascent = this.attribute('ascent').value;
  1825. this.descent = this.attribute('descent').value;
  1826. this.unitsPerEm = this.attribute('units-per-em').numValue();
  1827. }
  1828. svg.Element.fontface.prototype = new svg.Element.ElementBase;
  1829. // missing-glyph element
  1830. svg.Element.missingglyph = function (node) {
  1831. this.base = svg.Element.path;
  1832. this.base(node);
  1833. this.horizAdvX = 0;
  1834. }
  1835. svg.Element.missingglyph.prototype = new svg.Element.path;
  1836. // glyph element
  1837. svg.Element.glyph = function (node) {
  1838. this.base = svg.Element.path;
  1839. this.base(node);
  1840. this.horizAdvX = this.attribute('horiz-adv-x').numValue();
  1841. this.unicode = this.attribute('unicode').value;
  1842. this.arabicForm = this.attribute('arabic-form').value;
  1843. }
  1844. svg.Element.glyph.prototype = new svg.Element.path;
  1845. // text element
  1846. svg.Element.text = function (node) {
  1847. this.base = svg.Element.RenderedElementBase;
  1848. this.base(node);
  1849. if (node != null) {
  1850. // add children
  1851. this.children = [];
  1852. for (var i = 0; i < node.childNodes.length; i++) {
  1853. var childNode = node.childNodes[i];
  1854. if (childNode.nodeType == 1) { // capture tspan and tref nodes
  1855. this.addChild(childNode, true);
  1856. }
  1857. else if (childNode.nodeType == 3) { // capture text
  1858. this.addChild(new svg.Element.tspan(childNode), false);
  1859. }
  1860. }
  1861. }
  1862. this.baseSetContext = this.setContext;
  1863. this.setContext = function (ctx) {
  1864. this.baseSetContext(ctx);
  1865. if (this.style('dominant-baseline').hasValue()) ctx.textBaseline = this.style('dominant-baseline').value;
  1866. if (this.style('alignment-baseline').hasValue()) ctx.textBaseline = this.style('alignment-baseline').value;
  1867. }
  1868. this.renderChildren = function (ctx) {
  1869. var textAnchor = this.style('text-anchor').valueOrDefault('start');
  1870. var x = this.attribute('x').Length.toPixels('x');
  1871. var y = this.attribute('y').Length.toPixels('y');
  1872. for (var i = 0; i < this.children.length; i++) {
  1873. var child = this.children[i];
  1874. if (child.attribute('x').hasValue()) {
  1875. child.x = child.attribute('x').Length.toPixels('x');
  1876. }
  1877. else {
  1878. if (child.attribute('dx').hasValue()) x += child.attribute('dx').Length.toPixels('x');
  1879. child.x = x;
  1880. }
  1881. var childLength = child.measureText(ctx);
  1882. if (textAnchor != 'start' && (i == 0 || child.attribute('x').hasValue())) { // new group?
  1883. // loop through rest of children
  1884. var groupLength = childLength;
  1885. for (var j = i + 1; j < this.children.length; j++) {
  1886. var childInGroup = this.children[j];
  1887. if (childInGroup.attribute('x').hasValue()) break; // new group
  1888. groupLength += childInGroup.measureText(ctx);
  1889. }
  1890. child.x -= (textAnchor == 'end' ? groupLength : groupLength / 2.0);
  1891. }
  1892. x = child.x + childLength;
  1893. if (child.attribute('y').hasValue()) {
  1894. child.y = child.attribute('y').Length.toPixels('y');
  1895. }
  1896. else {
  1897. if (child.attribute('dy').hasValue()) y += child.attribute('dy').Length.toPixels('y');
  1898. child.y = y;
  1899. }
  1900. y = child.y;
  1901. child.render(ctx);
  1902. }
  1903. }
  1904. }
  1905. svg.Element.text.prototype = new svg.Element.RenderedElementBase;
  1906. // text base
  1907. svg.Element.TextElementBase = function (node) {
  1908. this.base = svg.Element.RenderedElementBase;
  1909. this.base(node);
  1910. this.getGlyph = function (font, text, i) {
  1911. var c = text[i];
  1912. var glyph = null;
  1913. if (font.isArabic) {
  1914. var arabicForm = 'isolated';
  1915. if ((i == 0 || text[i - 1] == ' ') && i < text.length - 2 && text[i + 1] != ' ') arabicForm = 'terminal';
  1916. if (i > 0 && text[i - 1] != ' ' && i < text.length - 2 && text[i + 1] != ' ') arabicForm = 'medial';
  1917. if (i > 0 && text[i - 1] != ' ' && (i == text.length - 1 || text[i + 1] == ' ')) arabicForm = 'initial';
  1918. if (typeof (font.glyphs[c]) != 'undefined') {
  1919. glyph = font.glyphs[c][arabicForm];
  1920. if (glyph == null && font.glyphs[c].type == 'glyph') glyph = font.glyphs[c];
  1921. }
  1922. }
  1923. else {
  1924. glyph = font.glyphs[c];
  1925. }
  1926. if (glyph == null) glyph = font.missingGlyph;
  1927. return glyph;
  1928. }
  1929. this.renderChildren = function (ctx) {
  1930. var customFont = this.parent.style('font-family').Definition.getDefinition();
  1931. if (customFont != null) {
  1932. var fontSize = this.parent.style('font-size').numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize);
  1933. var fontStyle = this.parent.style('font-style').valueOrDefault(svg.Font.Parse(svg.ctx.font).fontStyle);
  1934. var text = this.getText();
  1935. if (customFont.isRTL) text = text.split("").reverse().join("");
  1936. var dx = svg.ToNumberArray(this.parent.attribute('dx').value);
  1937. for (var i = 0; i < text.length; i++) {
  1938. var glyph = this.getGlyph(customFont, text, i);
  1939. var scale = fontSize / customFont.fontFace.unitsPerEm;
  1940. ctx.translate(this.x, this.y);
  1941. ctx.scale(scale, -scale);
  1942. var lw = ctx.lineWidth;
  1943. ctx.lineWidth = ctx.lineWidth * customFont.fontFace.unitsPerEm / fontSize;
  1944. if (fontStyle == 'italic') ctx.transform(1, 0, .4, 1, 0, 0);
  1945. glyph.render(ctx);
  1946. if (fontStyle == 'italic') ctx.transform(1, 0, -.4, 1, 0, 0);
  1947. ctx.lineWidth = lw;
  1948. ctx.scale(1 / scale, -1 / scale);
  1949. ctx.translate(-this.x, -this.y);
  1950. this.x += fontSize * (glyph.horizAdvX || customFont.horizAdvX) / customFont.fontFace.unitsPerEm;
  1951. if (typeof (dx[i]) != 'undefined' && !isNaN(dx[i])) {
  1952. this.x += dx[i];
  1953. }
  1954. }
  1955. return;
  1956. }
  1957. if (ctx.strokeStyle != '') ctx.strokeText(svg.compressSpaces(this.getText()), this.x, this.y);
  1958. if (ctx.fillStyle != '') ctx.fillText(svg.compressSpaces(this.getText()), this.x, this.y);
  1959. }
  1960. this.getText = function () {
  1961. // OVERRIDE ME
  1962. }
  1963. this.measureText = function (ctx) {
  1964. var customFont = this.parent.style('font-family').Definition.getDefinition();
  1965. if (customFont != null) {
  1966. var fontSize = this.parent.style('font-size').numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize);
  1967. var measure = 0;
  1968. var text = this.getText();
  1969. if (customFont.isRTL) text = text.split("").reverse().join("");
  1970. var dx = svg.ToNumberArray(this.parent.attribute('dx').value);
  1971. for (var i = 0; i < text.length; i++) {
  1972. var glyph = this.getGlyph(customFont, text, i);
  1973. measure += (glyph.horizAdvX || customFont.horizAdvX) * fontSize / customFont.fontFace.unitsPerEm;
  1974. if (typeof (dx[i]) != 'undefined' && !isNaN(dx[i])) {
  1975. measure += dx[i];
  1976. }
  1977. }
  1978. return measure;
  1979. }
  1980. var textToMeasure = svg.compressSpaces(this.getText());
  1981. if (!ctx.measureText) return textToMeasure.length * 10;
  1982. ctx.save();
  1983. this.setContext(ctx);
  1984. var width = ctx.measureText(textToMeasure).width;
  1985. ctx.restore();
  1986. return width;
  1987. }
  1988. }
  1989. svg.Element.TextElementBase.prototype = new svg.Element.RenderedElementBase;
  1990. // tspan
  1991. svg.Element.tspan = function (node) {
  1992. this.base = svg.Element.TextElementBase;
  1993. this.base(node);
  1994. this.text = node.nodeType == 3 ? node.nodeValue : // text
  1995. node.childNodes.length > 0 ? node.childNodes[0].nodeValue : // element
  1996. node.text;
  1997. this.getText = function () {
  1998. return this.text;
  1999. }
  2000. }
  2001. svg.Element.tspan.prototype = new svg.Element.TextElementBase;
  2002. // tref
  2003. svg.Element.tref = function (node) {
  2004. this.base = svg.Element.TextElementBase;
  2005. this.base(node);
  2006. this.getText = function () {
  2007. var element = this.attribute('xlink:href').Definition.getDefinition();
  2008. if (element != null) return element.children[0].getText();
  2009. }
  2010. }
  2011. svg.Element.tref.prototype = new svg.Element.TextElementBase;
  2012. // a element
  2013. svg.Element.a = function (node) {
  2014. this.base = svg.Element.TextElementBase;
  2015. this.base(node);
  2016. this.hasText = true;
  2017. for (var i = 0; i < node.childNodes.length; i++) {
  2018. if (node.childNodes[i].nodeType != 3) this.hasText = false;
  2019. }
  2020. // this might contain text
  2021. this.text = this.hasText ? node.childNodes[0].nodeValue : '';
  2022. this.getText = function () {
  2023. return this.text;
  2024. }
  2025. this.baseRenderChildren = this.renderChildren;
  2026. this.renderChildren = function (ctx) {
  2027. if (this.hasText) {
  2028. // render as text element
  2029. this.baseRenderChildren(ctx);
  2030. var fontSize = new svg.Property('fontSize', svg.Font.Parse(svg.ctx.font).fontSize);
  2031. svg.Mouse.checkBoundingBox(this, new svg.BoundingBox(this.x, this.y - fontSize.Length.toPixels('y'), this.x + this.measureText(ctx), this.y));
  2032. }
  2033. else {
  2034. // render as temporary group
  2035. var g = new svg.Element.g();
  2036. g.children = this.children;
  2037. g.parent = this;
  2038. g.render(ctx);
  2039. }
  2040. }
  2041. this.onclick = function () {
  2042. window.open(this.attribute('xlink:href').value);
  2043. }
  2044. this.onmousemove = function () {
  2045. svg.ctx.canvas.style.cursor = 'pointer';
  2046. }
  2047. }
  2048. svg.Element.a.prototype = new svg.Element.TextElementBase;
  2049. // image element
  2050. svg.Element.image = function (node) {
  2051. this.base = svg.Element.RenderedElementBase;
  2052. this.base(node);
  2053. svg.Images.push(this);
  2054. this.img = document.createElement('img');
  2055. this.loaded = false;
  2056. var that = this;
  2057. this.img.onload = function () { that.loaded = true; }
  2058. this.img.src = this.attribute('xlink:href').value;
  2059. this.renderChildren = function (ctx) {
  2060. var x = this.attribute('x').Length.toPixels('x');
  2061. var y = this.attribute('y').Length.toPixels('y');
  2062. var width = this.attribute('width').Length.toPixels('x');
  2063. var height = this.attribute('height').Length.toPixels('y');
  2064. if (width == 0 || height == 0) return;
  2065. ctx.save();
  2066. ctx.translate(x, y);
  2067. svg.AspectRatio(ctx,
  2068. this.attribute('preserveAspectRatio').value,
  2069. width,
  2070. this.img.width,
  2071. height,
  2072. this.img.height,
  2073. 0,
  2074. 0);
  2075. ctx.drawImage(this.img, 0, 0);
  2076. ctx.restore();
  2077. }
  2078. }
  2079. svg.Element.image.prototype = new svg.Element.RenderedElementBase;
  2080. // group element
  2081. svg.Element.g = function (node) {
  2082. this.base = svg.Element.RenderedElementBase;
  2083. this.base(node);
  2084. this.getBoundingBox = function () {
  2085. var bb = new svg.BoundingBox();
  2086. for (var i = 0; i < this.children.length; i++) {
  2087. bb.addBoundingBox(this.children[i].getBoundingBox());
  2088. }
  2089. return bb;
  2090. };
  2091. }
  2092. svg.Element.g.prototype = new svg.Element.RenderedElementBase;
  2093. // symbol element
  2094. svg.Element.symbol = function (node) {
  2095. this.base = svg.Element.RenderedElementBase;
  2096. this.base(node);
  2097. this.baseSetContext = this.setContext;
  2098. this.setContext = function (ctx) {
  2099. this.baseSetContext(ctx);
  2100. // viewbox
  2101. if (this.attribute('viewBox').hasValue()) {
  2102. var viewBox = svg.ToNumberArray(this.attribute('viewBox').value);
  2103. var minX = viewBox[0];
  2104. var minY = viewBox[1];
  2105. width = viewBox[2];
  2106. height = viewBox[3];
  2107. svg.AspectRatio(ctx,
  2108. this.attribute('preserveAspectRatio').value,
  2109. this.attribute('width').Length.toPixels('x'),
  2110. width,
  2111. this.attribute('height').Length.toPixels('y'),
  2112. height,
  2113. minX,
  2114. minY);
  2115. svg.ViewPort.SetCurrent(viewBox[2], viewBox[3]);
  2116. }
  2117. }
  2118. }
  2119. svg.Element.symbol.prototype = new svg.Element.RenderedElementBase;
  2120. // style element
  2121. svg.Element.style = function (node) {
  2122. this.base = svg.Element.ElementBase;
  2123. this.base(node);
  2124. // text, or spaces then CDATA
  2125. var css = node.childNodes[0].nodeValue + (node.childNodes.length > 1 ? node.childNodes[1].nodeValue : '');
  2126. css = css.replace(/(\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*+\/)|(^[\s]*\/\/.*)/gm, ''); // remove comments
  2127. css = svg.compressSpaces(css); // replace whitespace
  2128. var cssDefs = css.split('}');
  2129. for (var i = 0; i < cssDefs.length; i++) {
  2130. if (svg.trim(cssDefs[i]) != '') {
  2131. var cssDef = cssDefs[i].split('{');
  2132. var cssClasses = cssDef[0].split(',');
  2133. var cssProps = cssDef[1].split(';');
  2134. for (var j = 0; j < cssClasses.length; j++) {
  2135. var cssClass = svg.trim(cssClasses[j]);
  2136. if (cssClass != '') {
  2137. var props = {};
  2138. for (var k = 0; k < cssProps.length; k++) {
  2139. var prop = cssProps[k].indexOf(':');
  2140. var name = cssProps[k].substr(0, prop);
  2141. var value = cssProps[k].substr(prop + 1, cssProps[k].length - prop);
  2142. if (name != null && value != null) {
  2143. props[svg.trim(name)] = new svg.Property(svg.trim(name), svg.trim(value));
  2144. }
  2145. }
  2146. svg.Styles[cssClass] = props;
  2147. if (cssClass == '@font-face') {
  2148. var fontFamily = props['font-family'].value.replace(/"/g, '');
  2149. var srcs = props['src'].value.split(',');
  2150. for (var s = 0; s < srcs.length; s++) {
  2151. if (srcs[s].indexOf('format("svg")') > 0) {
  2152. var urlStart = srcs[s].indexOf('url');
  2153. var urlEnd = srcs[s].indexOf(')', urlStart);
  2154. var url = srcs[s].substr(urlStart + 5, urlEnd - urlStart - 6);
  2155. var doc = svg.parseXml(svg.ajax(url));
  2156. var fonts = doc.getElementsByTagName('font');
  2157. for (var f = 0; f < fonts.length; f++) {
  2158. var font = svg.CreateElement(fonts[f]);
  2159. svg.Definitions[fontFamily] = font;
  2160. }
  2161. }
  2162. }
  2163. }
  2164. }
  2165. }
  2166. }
  2167. }
  2168. }
  2169. svg.Element.style.prototype = new svg.Element.ElementBase;
  2170. // use element
  2171. svg.Element.use = function (node) {
  2172. this.base = svg.Element.RenderedElementBase;
  2173. this.base(node);
  2174. this.baseSetContext = this.setContext;
  2175. this.setContext = function (ctx) {
  2176. this.baseSetContext(ctx);
  2177. if (this.attribute('x').hasValue()) ctx.translate(this.attribute('x').Length.toPixels('x'), 0);
  2178. if (this.attribute('y').hasValue()) ctx.translate(0, this.attribute('y').Length.toPixels('y'));
  2179. }
  2180. this.getDefinition = function () {
  2181. var element = this.attribute('xlink:href').Definition.getDefinition();
  2182. if (this.attribute('width').hasValue()) element.attribute('width', true).value = this.attribute('width').value;
  2183. if (this.attribute('height').hasValue()) element.attribute('height', true).value = this.attribute('height').value;
  2184. return element;
  2185. }
  2186. this.path = function (ctx) {
  2187. var element = this.getDefinition();
  2188. if (element != null) element.path(ctx);
  2189. }
  2190. this.renderChildren = function (ctx) {
  2191. var element = this.getDefinition();
  2192. if (element != null) element.render(ctx);
  2193. }
  2194. }
  2195. svg.Element.use.prototype = new svg.Element.RenderedElementBase;
  2196. // mask element
  2197. svg.Element.mask = function (node) {
  2198. this.base = svg.Element.ElementBase;
  2199. this.base(node);
  2200. this.apply = function (ctx, element) {
  2201. // render as temp svg
  2202. var x = this.attribute('x').Length.toPixels('x');
  2203. var y = this.attribute('y').Length.toPixels('y');
  2204. var width = this.attribute('width').Length.toPixels('x');
  2205. var height = this.attribute('height').Length.toPixels('y');
  2206. // temporarily remove mask to avoid recursion
  2207. var mask = element.attribute('mask').value;
  2208. element.attribute('mask').value = '';
  2209. var cMask = document.createElement('canvas');
  2210. cMask.width = x + width;
  2211. cMask.height = y + height;
  2212. var maskCtx = cMask.getContext('2d');
  2213. this.renderChildren(maskCtx);
  2214. var c = document.createElement('canvas');
  2215. c.width = x + width;
  2216. c.height = y + height;
  2217. var tempCtx = c.getContext('2d');
  2218. element.render(tempCtx);
  2219. tempCtx.globalCompositeOperation = 'destination-in';
  2220. tempCtx.fillStyle = maskCtx.createPattern(cMask, 'no-repeat');
  2221. tempCtx.fillRect(0, 0, x + width, y + height);
  2222. ctx.fillStyle = tempCtx.createPattern(c, 'no-repeat');
  2223. ctx.fillRect(0, 0, x + width, y + height);
  2224. // reassign mask
  2225. element.attribute('mask').value = mask;
  2226. }
  2227. this.render = function (ctx) {
  2228. // NO RENDER
  2229. }
  2230. }
  2231. svg.Element.mask.prototype = new svg.Element.ElementBase;
  2232. // clip element
  2233. svg.Element.clipPath = function (node) {
  2234. this.base = svg.Element.ElementBase;
  2235. this.base(node);
  2236. this.apply = function (ctx) {
  2237. for (var i = 0; i < this.children.length; i++) {
  2238. if (this.children[i].path) {
  2239. this.children[i].path(ctx);
  2240. ctx.clip();
  2241. }
  2242. }
  2243. }
  2244. this.render = function (ctx) {
  2245. // NO RENDER
  2246. }
  2247. }
  2248. svg.Element.clipPath.prototype = new svg.Element.ElementBase;
  2249. // filters
  2250. svg.Element.filter = function (node) {
  2251. this.base = svg.Element.ElementBase;
  2252. this.base(node);
  2253. this.apply = function (ctx, element) {
  2254. // render as temp svg
  2255. var bb = element.getBoundingBox();
  2256. var x = this.attribute('x').Length.toPixels('x');
  2257. var y = this.attribute('y').Length.toPixels('y');
  2258. if (x == 0 || y == 0) {
  2259. x = bb.x1;
  2260. y = bb.y1;
  2261. }
  2262. var width = this.attribute('width').Length.toPixels('x');
  2263. var height = this.attribute('height').Length.toPixels('y');
  2264. if (width == 0 || height == 0) {
  2265. width = bb.width();
  2266. height = bb.height();
  2267. }
  2268. // temporarily remove filter to avoid recursion
  2269. var filter = element.style('filter').value;
  2270. element.style('filter').value = '';
  2271. // max filter distance
  2272. var extraPercent = .20;
  2273. var px = extraPercent * width;
  2274. var py = extraPercent * height;
  2275. var c = document.createElement('canvas');
  2276. c.width = width + 2 * px;
  2277. c.height = height + 2 * py;
  2278. var tempCtx = c.getContext('2d');
  2279. tempCtx.translate(-x + px, -y + py);
  2280. element.render(tempCtx);
  2281. // apply filters
  2282. for (var i = 0; i < this.children.length; i++) {
  2283. this.children[i].apply(tempCtx, 0, 0, width + 2 * px, height + 2 * py);
  2284. }
  2285. // render on me
  2286. ctx.drawImage(c, 0, 0, width + 2 * px, height + 2 * py, x - px, y - py, width + 2 * px, height + 2 * py);
  2287. // reassign filter
  2288. element.style('filter', true).value = filter;
  2289. }
  2290. this.render = function (ctx) {
  2291. // NO RENDER
  2292. }
  2293. }
  2294. svg.Element.filter.prototype = new svg.Element.ElementBase;
  2295. svg.Element.feGaussianBlur = function (node) {
  2296. this.base = svg.Element.ElementBase;
  2297. this.base(node);
  2298. function make_fgauss(sigma) {
  2299. sigma = Math.max(sigma, 0.01);
  2300. var len = Math.ceil(sigma * 4.0) + 1;
  2301. mask = [];
  2302. for (var i = 0; i < len; i++) {
  2303. mask[i] = Math.exp(-0.5 * (i / sigma) * (i / sigma));
  2304. }
  2305. return mask;
  2306. }
  2307. function normalize(mask) {
  2308. var sum = 0;
  2309. for (var i = 1; i < mask.length; i++) {
  2310. sum += Math.abs(mask[i]);
  2311. }
  2312. sum = 2 * sum + Math.abs(mask[0]);
  2313. for (var i = 0; i < mask.length; i++) {
  2314. mask[i] /= sum;
  2315. }
  2316. return mask;
  2317. }
  2318. function convolve_even(src, dst, mask, width, height) {
  2319. for (var y = 0; y < height; y++) {
  2320. for (var x = 0; x < width; x++) {
  2321. var a = imGet(src, x, y, width, height, 3) / 255;
  2322. for (var rgba = 0; rgba < 4; rgba++) {
  2323. var sum = mask[0] * (a == 0 ? 255 : imGet(src, x, y, width, height, rgba)) * (a == 0 || rgba == 3 ? 1 : a);
  2324. for (var i = 1; i < mask.length; i++) {
  2325. var a1 = imGet(src, Math.max(x - i, 0), y, width, height, 3) / 255;
  2326. var a2 = imGet(src, Math.min(x + i, width - 1), y, width, height, 3) / 255;
  2327. sum += mask[i] *
  2328. ((a1 == 0 ? 255 : imGet(src, Math.max(x - i, 0), y, width, height, rgba)) * (a1 == 0 || rgba == 3 ? 1 : a1) +
  2329. (a2 == 0 ? 255 : imGet(src, Math.min(x + i, width - 1), y, width, height, rgba)) * (a2 == 0 || rgba == 3 ? 1 : a2));
  2330. }
  2331. imSet(dst, y, x, height, width, rgba, sum);
  2332. }
  2333. }
  2334. }
  2335. }
  2336. function imGet(img, x, y, width, height, rgba) {
  2337. return img[y * width * 4 + x * 4 + rgba];
  2338. }
  2339. function imSet(img, x, y, width, height, rgba, val) {
  2340. img[y * width * 4 + x * 4 + rgba] = val;
  2341. }
  2342. function blur(ctx, width, height, sigma) {
  2343. var srcData = ctx.getImageData(0, 0, width, height);
  2344. var mask = make_fgauss(sigma);
  2345. mask = normalize(mask);
  2346. tmp = [];
  2347. convolve_even(srcData.data, tmp, mask, width, height);
  2348. convolve_even(tmp, srcData.data, mask, height, width);
  2349. ctx.clearRect(0, 0, width, height);
  2350. ctx.putImageData(srcData, 0, 0);
  2351. }
  2352. this.apply = function (ctx, x, y, width, height) {
  2353. // assuming x==0 && y==0 for now
  2354. blur(ctx, width, height, this.attribute('stdDeviation').numValue());
  2355. }
  2356. }
  2357. svg.Element.filter.prototype = new svg.Element.feGaussianBlur;
  2358. // title element, do nothing
  2359. svg.Element.title = function (node) {
  2360. }
  2361. svg.Element.title.prototype = new svg.Element.ElementBase;
  2362. // desc element, do nothing
  2363. svg.Element.desc = function (node) {
  2364. }
  2365. svg.Element.desc.prototype = new svg.Element.ElementBase;
  2366. svg.Element.MISSING = function (node) {
  2367. console.log('ERROR: Element \'' + node.nodeName + '\' not yet implemented.');
  2368. }
  2369. svg.Element.MISSING.prototype = new svg.Element.ElementBase;
  2370. // element factory
  2371. svg.CreateElement = function (node) {
  2372. var className = node.nodeName.replace(/^[^:]+:/, ''); // remove namespace
  2373. className = className.replace(/\-/g, ''); // remove dashes
  2374. var e = null;
  2375. if (typeof (svg.Element[className]) != 'undefined') {
  2376. e = new svg.Element[className](node);
  2377. }
  2378. else {
  2379. e = new svg.Element.MISSING(node);
  2380. }
  2381. e.type = node.nodeName;
  2382. return e;
  2383. }
  2384. // load from url
  2385. svg.load = function (ctx, url) {
  2386. svg.loadXml(ctx, svg.ajax(url));
  2387. }
  2388. // load from xml
  2389. svg.loadXml = function (ctx, xml) {
  2390. svg.loadXmlDoc(ctx, svg.parseXml(xml));
  2391. }
  2392. svg.loadXmlDoc = function (ctx, dom) {
  2393. svg.init(ctx);
  2394. var mapXY = function (p) {
  2395. var e = ctx.canvas;
  2396. while (e) {
  2397. p.x -= e.offsetLeft;
  2398. p.y -= e.offsetTop;
  2399. e = e.offsetParent;
  2400. }
  2401. if (window.scrollX) p.x += window.scrollX;
  2402. if (window.scrollY) p.y += window.scrollY;
  2403. return p;
  2404. }
  2405. // bind mouse
  2406. if (svg.opts['ignoreMouse'] != true) {
  2407. ctx.canvas.onclick = function (e) {
  2408. var p = mapXY(new svg.Point(e != null ? e.clientX : event.clientX, e != null ? e.clientY : event.clientY));
  2409. svg.Mouse.onclick(p.x, p.y);
  2410. };
  2411. ctx.canvas.onmousemove = function (e) {
  2412. var p = mapXY(new svg.Point(e != null ? e.clientX : event.clientX, e != null ? e.clientY : event.clientY));
  2413. svg.Mouse.onmousemove(p.x, p.y);
  2414. };
  2415. }
  2416. var e = svg.CreateElement(dom.documentElement);
  2417. e.root = true;
  2418. // render loop
  2419. var isFirstRender = true;
  2420. var draw = function () {
  2421. svg.ViewPort.Clear();
  2422. if (ctx.canvas.parentNode) svg.ViewPort.SetCurrent(ctx.canvas.parentNode.clientWidth, ctx.canvas.parentNode.clientHeight);
  2423. if (svg.opts['ignoreDimensions'] != true) {
  2424. // set canvas size
  2425. if (e.style('width').hasValue()) {
  2426. ctx.canvas.width = e.style('width').Length.toPixels('x');
  2427. ctx.canvas.style.width = ctx.canvas.width + 'px';
  2428. }
  2429. if (e.style('height').hasValue()) {
  2430. ctx.canvas.height = e.style('height').Length.toPixels('y');
  2431. ctx.canvas.style.height = ctx.canvas.height + 'px';
  2432. }
  2433. }
  2434. var cWidth = ctx.canvas.clientWidth || ctx.canvas.width;
  2435. var cHeight = ctx.canvas.clientHeight || ctx.canvas.height;
  2436. svg.ViewPort.SetCurrent(cWidth, cHeight);
  2437. if (svg.opts != null && svg.opts['offsetX'] != null) e.attribute('x', true).value = svg.opts['offsetX'];
  2438. if (svg.opts != null && svg.opts['offsetY'] != null) e.attribute('y', true).value = svg.opts['offsetY'];
  2439. if (svg.opts != null && svg.opts['scaleWidth'] != null && svg.opts['scaleHeight'] != null) {
  2440. var xRatio = 1, yRatio = 1;
  2441. if (e.attribute('width').hasValue()) xRatio = e.attribute('width').Length.toPixels('x') / svg.opts['scaleWidth'];
  2442. if (e.attribute('height').hasValue()) yRatio = e.attribute('height').Length.toPixels('y') / svg.opts['scaleHeight'];
  2443. e.attribute('width', true).value = svg.opts['scaleWidth'];
  2444. e.attribute('height', true).value = svg.opts['scaleHeight'];
  2445. e.attribute('viewBox', true).value = '0 0 ' + (cWidth * xRatio) + ' ' + (cHeight * yRatio);
  2446. e.attribute('preserveAspectRatio', true).value = 'none';
  2447. }
  2448. // clear and render
  2449. if (svg.opts['ignoreClear'] != true) {
  2450. ctx.clearRect(0, 0, cWidth, cHeight);
  2451. }
  2452. e.render(ctx);
  2453. if (isFirstRender) {
  2454. isFirstRender = false;
  2455. if (svg.opts != null && typeof (svg.opts['renderCallback']) == 'function') svg.opts['renderCallback']();
  2456. }
  2457. }
  2458. var waitingForImages = true;
  2459. if (svg.ImagesLoaded()) {
  2460. waitingForImages = false;
  2461. draw();
  2462. }
  2463. svg.intervalID = setInterval(function () {
  2464. var needUpdate = false;
  2465. if (waitingForImages && svg.ImagesLoaded()) {
  2466. waitingForImages = false;
  2467. needUpdate = true;
  2468. }
  2469. // need update from mouse events?
  2470. if (svg.opts['ignoreMouse'] != true) {
  2471. needUpdate = needUpdate | svg.Mouse.hasEvents();
  2472. }
  2473. // need update from animations?
  2474. if (svg.opts['ignoreAnimation'] != true) {
  2475. for (var i = 0; i < svg.Animations.length; i++) {
  2476. needUpdate = needUpdate | svg.Animations[i].update(1000 / svg.FRAMERATE);
  2477. }
  2478. }
  2479. // need update from redraw?
  2480. if (svg.opts != null && typeof (svg.opts['forceRedraw']) == 'function') {
  2481. if (svg.opts['forceRedraw']() == true) needUpdate = true;
  2482. }
  2483. // render if needed
  2484. if (needUpdate) {
  2485. draw();
  2486. svg.Mouse.runEvents(); // run and clear our events
  2487. }
  2488. }, 1000 / svg.FRAMERATE);
  2489. }
  2490. svg.stop = function () {
  2491. if (svg.intervalID) {
  2492. clearInterval(svg.intervalID);
  2493. }
  2494. }
  2495. svg.Mouse = new (function () {
  2496. this.events = [];
  2497. this.hasEvents = function () { return this.events.length != 0; }
  2498. this.onclick = function (x, y) {
  2499. this.events.push({
  2500. type: 'onclick', x: x, y: y,
  2501. run: function (e) { if (e.onclick) e.onclick(); }
  2502. });
  2503. }
  2504. this.onmousemove = function (x, y) {
  2505. this.events.push({
  2506. type: 'onmousemove', x: x, y: y,
  2507. run: function (e) { if (e.onmousemove) e.onmousemove(); }
  2508. });
  2509. }
  2510. this.eventElements = [];
  2511. this.checkPath = function (element, ctx) {
  2512. for (var i = 0; i < this.events.length; i++) {
  2513. var e = this.events[i];
  2514. if (ctx.isPointInPath && ctx.isPointInPath(e.x, e.y)) this.eventElements[i] = element;
  2515. }
  2516. }
  2517. this.checkBoundingBox = function (element, bb) {
  2518. for (var i = 0; i < this.events.length; i++) {
  2519. var e = this.events[i];
  2520. if (bb.isPointInBox(e.x, e.y)) this.eventElements[i] = element;
  2521. }
  2522. }
  2523. this.runEvents = function () {
  2524. svg.ctx.canvas.style.cursor = '';
  2525. for (var i = 0; i < this.events.length; i++) {
  2526. var e = this.events[i];
  2527. var element = this.eventElements[i];
  2528. while (element) {
  2529. e.run(element);
  2530. element = element.parent;
  2531. }
  2532. }
  2533. // done running, clear
  2534. this.events = [];
  2535. this.eventElements = [];
  2536. }
  2537. });
  2538. return svg;
  2539. }
  2540. })();
  2541. if (CanvasRenderingContext2D) {
  2542. CanvasRenderingContext2D.prototype.drawSvg = function (s, dx, dy, dw, dh) {
  2543. canvg(this.canvas, s, {
  2544. ignoreMouse: true,
  2545. ignoreAnimation: true,
  2546. ignoreDimensions: true,
  2547. ignoreClear: true,
  2548. offsetX: dx,
  2549. offsetY: dy,
  2550. scaleWidth: dw,
  2551. scaleHeight: dh
  2552. });
  2553. }
  2554. }/**
  2555. * @license Highcharts JS v3.0.6 (2013-10-04)
  2556. * CanVGRenderer Extension module
  2557. *
  2558. * (c) 2011-2012 Torstein Hønsi, Erik Olsson
  2559. *
  2560. * License: www.highcharts.com/license
  2561. */
  2562. // JSLint options:
  2563. /*global Highcharts */
  2564. (function (Highcharts) { // encapsulate
  2565. var UNDEFINED,
  2566. DIV = 'div',
  2567. ABSOLUTE = 'absolute',
  2568. RELATIVE = 'relative',
  2569. HIDDEN = 'hidden',
  2570. VISIBLE = 'visible',
  2571. PX = 'px',
  2572. css = Highcharts.css,
  2573. CanVGRenderer = Highcharts.CanVGRenderer,
  2574. SVGRenderer = Highcharts.SVGRenderer,
  2575. extend = Highcharts.extend,
  2576. merge = Highcharts.merge,
  2577. addEvent = Highcharts.addEvent,
  2578. createElement = Highcharts.createElement,
  2579. discardElement = Highcharts.discardElement;
  2580. // Extend CanVG renderer on demand, inherit from SVGRenderer
  2581. extend(CanVGRenderer.prototype, SVGRenderer.prototype);
  2582. // Add additional functionality:
  2583. extend(CanVGRenderer.prototype, {
  2584. create: function (chart, container, chartWidth, chartHeight) {
  2585. this.setContainer(container, chartWidth, chartHeight);
  2586. this.configure(chart);
  2587. },
  2588. setContainer: function (container, chartWidth, chartHeight) {
  2589. var containerStyle = container.style,
  2590. containerParent = container.parentNode,
  2591. containerLeft = containerStyle.left,
  2592. containerTop = containerStyle.top,
  2593. containerOffsetWidth = container.offsetWidth,
  2594. containerOffsetHeight = container.offsetHeight,
  2595. canvas,
  2596. initialHiddenStyle = { visibility: HIDDEN, position: ABSOLUTE };
  2597. this.init.apply(this, [container, chartWidth, chartHeight]);
  2598. // add the canvas above it
  2599. canvas = createElement('canvas', {
  2600. width: containerOffsetWidth,
  2601. height: containerOffsetHeight
  2602. }, {
  2603. position: RELATIVE,
  2604. left: containerLeft,
  2605. top: containerTop
  2606. }, container);
  2607. this.canvas = canvas;
  2608. // Create the tooltip line and div, they are placed as siblings to
  2609. // the container (and as direct childs to the div specified in the html page)
  2610. this.ttLine = createElement(DIV, null, initialHiddenStyle, containerParent);
  2611. this.ttDiv = createElement(DIV, null, initialHiddenStyle, containerParent);
  2612. this.ttTimer = UNDEFINED;
  2613. // Move away the svg node to a new div inside the container's parent so we can hide it.
  2614. var hiddenSvg = createElement(DIV, {
  2615. width: containerOffsetWidth,
  2616. height: containerOffsetHeight
  2617. }, {
  2618. visibility: HIDDEN,
  2619. left: containerLeft,
  2620. top: containerTop
  2621. }, containerParent);
  2622. this.hiddenSvg = hiddenSvg;
  2623. hiddenSvg.appendChild(this.box);
  2624. },
  2625. /**
  2626. * Configures the renderer with the chart. Attach a listener to the event tooltipRefresh.
  2627. **/
  2628. configure: function (chart) {
  2629. var renderer = this,
  2630. options = chart.options.tooltip,
  2631. borderWidth = options.borderWidth,
  2632. tooltipDiv = renderer.ttDiv,
  2633. tooltipDivStyle = options.style,
  2634. tooltipLine = renderer.ttLine,
  2635. padding = parseInt(tooltipDivStyle.padding, 10);
  2636. // Add border styling from options to the style
  2637. tooltipDivStyle = merge(tooltipDivStyle, {
  2638. padding: padding + PX,
  2639. 'background-color': options.backgroundColor,
  2640. 'border-style': 'solid',
  2641. 'border-width': borderWidth + PX,
  2642. 'border-radius': options.borderRadius + PX
  2643. });
  2644. // Optionally add shadow
  2645. if (options.shadow) {
  2646. tooltipDivStyle = merge(tooltipDivStyle, {
  2647. 'box-shadow': '1px 1px 3px gray', // w3c
  2648. '-webkit-box-shadow': '1px 1px 3px gray' // webkit
  2649. });
  2650. }
  2651. css(tooltipDiv, tooltipDivStyle);
  2652. // Set simple style on the line
  2653. css(tooltipLine, {
  2654. 'border-left': '1px solid darkgray'
  2655. });
  2656. // This event is triggered when a new tooltip should be shown
  2657. addEvent(chart, 'tooltipRefresh', function (args) {
  2658. var chartContainer = chart.container,
  2659. offsetLeft = chartContainer.offsetLeft,
  2660. offsetTop = chartContainer.offsetTop,
  2661. position;
  2662. // Set the content of the tooltip
  2663. tooltipDiv.innerHTML = args.text;
  2664. // Compute the best position for the tooltip based on the divs size and container size.
  2665. position = chart.tooltip.getPosition(tooltipDiv.offsetWidth, tooltipDiv.offsetHeight, { plotX: args.x, plotY: args.y });
  2666. css(tooltipDiv, {
  2667. visibility: VISIBLE,
  2668. left: position.x + PX,
  2669. top: position.y + PX,
  2670. 'border-color': args.borderColor
  2671. });
  2672. // Position the tooltip line
  2673. css(tooltipLine, {
  2674. visibility: VISIBLE,
  2675. left: offsetLeft + args.x + PX,
  2676. top: offsetTop + chart.plotTop + PX,
  2677. height: chart.plotHeight + PX
  2678. });
  2679. // This timeout hides the tooltip after 3 seconds
  2680. // First clear any existing timer
  2681. if (renderer.ttTimer !== UNDEFINED) {
  2682. clearTimeout(renderer.ttTimer);
  2683. }
  2684. // Start a new timer that hides tooltip and line
  2685. renderer.ttTimer = setTimeout(function () {
  2686. css(tooltipDiv, { visibility: HIDDEN });
  2687. css(tooltipLine, { visibility: HIDDEN });
  2688. }, 3000);
  2689. });
  2690. },
  2691. /**
  2692. * Extend SVGRenderer.destroy to also destroy the elements added by CanVGRenderer.
  2693. */
  2694. destroy: function () {
  2695. var renderer = this;
  2696. // Remove the canvas
  2697. discardElement(renderer.canvas);
  2698. // Kill the timer
  2699. if (renderer.ttTimer !== UNDEFINED) {
  2700. clearTimeout(renderer.ttTimer);
  2701. }
  2702. // Remove the divs for tooltip and line
  2703. discardElement(renderer.ttLine);
  2704. discardElement(renderer.ttDiv);
  2705. discardElement(renderer.hiddenSvg);
  2706. // Continue with base class
  2707. return SVGRenderer.prototype.destroy.apply(renderer);
  2708. },
  2709. /**
  2710. * Take a color and return it if it's a string, do not make it a gradient even if it is a
  2711. * gradient. Currently canvg cannot render gradients (turns out black),
  2712. * see: http://code.google.com/p/canvg/issues/detail?id=104
  2713. *
  2714. * @param {Object} color The color or config object
  2715. */
  2716. color: function (color, elem, prop) {
  2717. if (color && color.linearGradient) {
  2718. // Pick the end color and forward to base implementation
  2719. color = color.stops[color.stops.length - 1][1];
  2720. }
  2721. return SVGRenderer.prototype.color.call(this, color, elem, prop);
  2722. },
  2723. /**
  2724. * Draws the SVG on the canvas or adds a draw invokation to the deferred list.
  2725. */
  2726. draw: function () {
  2727. var renderer = this;
  2728. window.canvg(renderer.canvas, renderer.hiddenSvg.innerHTML);
  2729. }
  2730. });
  2731. }(Highcharts));