PageRenderTime 56ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/files/highstocks/1.3.7/modules/canvas-tools.src.js

https://gitlab.com/Mirros/jsdelivr
JavaScript | 1585 lines | 1294 code | 173 blank | 118 comment | 409 complexity | ccf312e05b5cb987ba7d71219363a74c MD5 | raw file
  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. {
  10. this.ok = false;
  11. // strip any leading #
  12. if (color_string.charAt(0) == '#') { // remove # if any
  13. color_string = color_string.substr(1,6);
  14. }
  15. color_string = color_string.replace(/ /g,'');
  16. color_string = color_string.toLowerCase();
  17. // before getting into regexps, try simple matches
  18. // and overwrite the input
  19. var simple_colors = {
  20. aliceblue: 'f0f8ff',
  21. antiquewhite: 'faebd7',
  22. aqua: '00ffff',
  23. aquamarine: '7fffd4',
  24. azure: 'f0ffff',
  25. beige: 'f5f5dc',
  26. bisque: 'ffe4c4',
  27. black: '000000',
  28. blanchedalmond: 'ffebcd',
  29. blue: '0000ff',
  30. blueviolet: '8a2be2',
  31. brown: 'a52a2a',
  32. burlywood: 'deb887',
  33. cadetblue: '5f9ea0',
  34. chartreuse: '7fff00',
  35. chocolate: 'd2691e',
  36. coral: 'ff7f50',
  37. cornflowerblue: '6495ed',
  38. cornsilk: 'fff8dc',
  39. crimson: 'dc143c',
  40. cyan: '00ffff',
  41. darkblue: '00008b',
  42. darkcyan: '008b8b',
  43. darkgoldenrod: 'b8860b',
  44. darkgray: 'a9a9a9',
  45. darkgreen: '006400',
  46. darkkhaki: 'bdb76b',
  47. darkmagenta: '8b008b',
  48. darkolivegreen: '556b2f',
  49. darkorange: 'ff8c00',
  50. darkorchid: '9932cc',
  51. darkred: '8b0000',
  52. darksalmon: 'e9967a',
  53. darkseagreen: '8fbc8f',
  54. darkslateblue: '483d8b',
  55. darkslategray: '2f4f4f',
  56. darkturquoise: '00ced1',
  57. darkviolet: '9400d3',
  58. deeppink: 'ff1493',
  59. deepskyblue: '00bfff',
  60. dimgray: '696969',
  61. dodgerblue: '1e90ff',
  62. feldspar: 'd19275',
  63. firebrick: 'b22222',
  64. floralwhite: 'fffaf0',
  65. forestgreen: '228b22',
  66. fuchsia: 'ff00ff',
  67. gainsboro: 'dcdcdc',
  68. ghostwhite: 'f8f8ff',
  69. gold: 'ffd700',
  70. goldenrod: 'daa520',
  71. gray: '808080',
  72. green: '008000',
  73. greenyellow: 'adff2f',
  74. honeydew: 'f0fff0',
  75. hotpink: 'ff69b4',
  76. indianred : 'cd5c5c',
  77. indigo : '4b0082',
  78. ivory: 'fffff0',
  79. khaki: 'f0e68c',
  80. lavender: 'e6e6fa',
  81. lavenderblush: 'fff0f5',
  82. lawngreen: '7cfc00',
  83. lemonchiffon: 'fffacd',
  84. lightblue: 'add8e6',
  85. lightcoral: 'f08080',
  86. lightcyan: 'e0ffff',
  87. lightgoldenrodyellow: 'fafad2',
  88. lightgrey: 'd3d3d3',
  89. lightgreen: '90ee90',
  90. lightpink: 'ffb6c1',
  91. lightsalmon: 'ffa07a',
  92. lightseagreen: '20b2aa',
  93. lightskyblue: '87cefa',
  94. lightslateblue: '8470ff',
  95. lightslategray: '778899',
  96. lightsteelblue: 'b0c4de',
  97. lightyellow: 'ffffe0',
  98. lime: '00ff00',
  99. limegreen: '32cd32',
  100. linen: 'faf0e6',
  101. magenta: 'ff00ff',
  102. maroon: '800000',
  103. mediumaquamarine: '66cdaa',
  104. mediumblue: '0000cd',
  105. mediumorchid: 'ba55d3',
  106. mediumpurple: '9370d8',
  107. mediumseagreen: '3cb371',
  108. mediumslateblue: '7b68ee',
  109. mediumspringgreen: '00fa9a',
  110. mediumturquoise: '48d1cc',
  111. mediumvioletred: 'c71585',
  112. midnightblue: '191970',
  113. mintcream: 'f5fffa',
  114. mistyrose: 'ffe4e1',
  115. moccasin: 'ffe4b5',
  116. navajowhite: 'ffdead',
  117. navy: '000080',
  118. oldlace: 'fdf5e6',
  119. olive: '808000',
  120. olivedrab: '6b8e23',
  121. orange: 'ffa500',
  122. orangered: 'ff4500',
  123. orchid: 'da70d6',
  124. palegoldenrod: 'eee8aa',
  125. palegreen: '98fb98',
  126. paleturquoise: 'afeeee',
  127. palevioletred: 'd87093',
  128. papayawhip: 'ffefd5',
  129. peachpuff: 'ffdab9',
  130. peru: 'cd853f',
  131. pink: 'ffc0cb',
  132. plum: 'dda0dd',
  133. powderblue: 'b0e0e6',
  134. purple: '800080',
  135. red: 'ff0000',
  136. rosybrown: 'bc8f8f',
  137. royalblue: '4169e1',
  138. saddlebrown: '8b4513',
  139. salmon: 'fa8072',
  140. sandybrown: 'f4a460',
  141. seagreen: '2e8b57',
  142. seashell: 'fff5ee',
  143. sienna: 'a0522d',
  144. silver: 'c0c0c0',
  145. skyblue: '87ceeb',
  146. slateblue: '6a5acd',
  147. slategray: '708090',
  148. snow: 'fffafa',
  149. springgreen: '00ff7f',
  150. steelblue: '4682b4',
  151. tan: 'd2b48c',
  152. teal: '008080',
  153. thistle: 'd8bfd8',
  154. tomato: 'ff6347',
  155. turquoise: '40e0d0',
  156. violet: 'ee82ee',
  157. violetred: 'd02090',
  158. wheat: 'f5deb3',
  159. white: 'ffffff',
  160. whitesmoke: 'f5f5f5',
  161. yellow: 'ffff00',
  162. yellowgreen: '9acd32'
  163. };
  164. for (var key in simple_colors) {
  165. if (color_string == key) {
  166. color_string = simple_colors[key];
  167. }
  168. }
  169. // emd of simple type-in colors
  170. // array of color definition objects
  171. var color_defs = [
  172. {
  173. re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,
  174. example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'],
  175. process: function (bits){
  176. return [
  177. parseInt(bits[1]),
  178. parseInt(bits[2]),
  179. parseInt(bits[3])
  180. ];
  181. }
  182. },
  183. {
  184. re: /^(\w{2})(\w{2})(\w{2})$/,
  185. example: ['#00ff00', '336699'],
  186. process: function (bits){
  187. return [
  188. parseInt(bits[1], 16),
  189. parseInt(bits[2], 16),
  190. parseInt(bits[3], 16)
  191. ];
  192. }
  193. },
  194. {
  195. re: /^(\w{1})(\w{1})(\w{1})$/,
  196. example: ['#fb0', 'f0f'],
  197. process: function (bits){
  198. return [
  199. parseInt(bits[1] + bits[1], 16),
  200. parseInt(bits[2] + bits[2], 16),
  201. parseInt(bits[3] + bits[3], 16)
  202. ];
  203. }
  204. }
  205. ];
  206. // search through the definitions to find a match
  207. for (var i = 0; i < color_defs.length; i++) {
  208. var re = color_defs[i].re;
  209. var processor = color_defs[i].process;
  210. var bits = re.exec(color_string);
  211. if (bits) {
  212. channels = processor(bits);
  213. this.r = channels[0];
  214. this.g = channels[1];
  215. this.b = channels[2];
  216. this.ok = true;
  217. }
  218. }
  219. // validate/cleanup values
  220. this.r = (this.r < 0 || isNaN(this.r)) ? 0 : ((this.r > 255) ? 255 : this.r);
  221. this.g = (this.g < 0 || isNaN(this.g)) ? 0 : ((this.g > 255) ? 255 : this.g);
  222. this.b = (this.b < 0 || isNaN(this.b)) ? 0 : ((this.b > 255) ? 255 : this.b);
  223. // some getters
  224. this.toRGB = function () {
  225. return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')';
  226. }
  227. this.toHex = function () {
  228. var r = this.r.toString(16);
  229. var g = this.g.toString(16);
  230. var b = this.b.toString(16);
  231. if (r.length == 1) r = '0' + r;
  232. if (g.length == 1) g = '0' + g;
  233. if (b.length == 1) b = '0' + b;
  234. return '#' + r + g + b;
  235. }
  236. // help
  237. this.getHelpXML = function () {
  238. var examples = new Array();
  239. // add regexps
  240. for (var i = 0; i < color_defs.length; i++) {
  241. var example = color_defs[i].example;
  242. for (var j = 0; j < example.length; j++) {
  243. examples[examples.length] = example[j];
  244. }
  245. }
  246. // add type-in colors
  247. for (var sc in simple_colors) {
  248. examples[examples.length] = sc;
  249. }
  250. var xml = document.createElement('ul');
  251. xml.setAttribute('id', 'rgbcolor-examples');
  252. for (var i = 0; i < examples.length; i++) {
  253. try {
  254. var list_item = document.createElement('li');
  255. var list_color = new RGBColor(examples[i]);
  256. var example_div = document.createElement('div');
  257. example_div.style.cssText =
  258. 'margin: 3px; '
  259. + 'border: 1px solid black; '
  260. + 'background:' + list_color.toHex() + '; '
  261. + 'color:' + list_color.toHex()
  262. ;
  263. example_div.appendChild(document.createTextNode('test'));
  264. var list_item_value = document.createTextNode(
  265. ' ' + examples[i] + ' -> ' + list_color.toRGB() + ' -> ' + list_color.toHex()
  266. );
  267. list_item.appendChild(example_div);
  268. list_item.appendChild(list_item_value);
  269. xml.appendChild(list_item);
  270. } catch(e){}
  271. }
  272. return xml;
  273. }
  274. }
  275. /**
  276. * @license canvg.js - Javascript SVG parser and renderer on Canvas
  277. * MIT Licensed
  278. * Gabe Lerner (gabelerner@gmail.com)
  279. * http://code.google.com/p/canvg/
  280. *
  281. * Requires: rgbcolor.js - http://www.phpied.com/rgb-color-parser-in-javascript/
  282. *
  283. */
  284. if(!window.console) {
  285. window.console = {};
  286. window.console.log = function(str) {};
  287. window.console.dir = function(str) {};
  288. }
  289. if(!Array.prototype.indexOf){
  290. Array.prototype.indexOf = function(obj){
  291. for(var i=0; i<this.length; i++){
  292. if(this[i]==obj){
  293. return i;
  294. }
  295. }
  296. return -1;
  297. }
  298. }
  299. (function(){
  300. // canvg(target, s)
  301. // empty parameters: replace all 'svg' elements on page with 'canvas' elements
  302. // target: canvas element or the id of a canvas element
  303. // s: svg string, url to svg file, or xml document
  304. // opts: optional hash of options
  305. // ignoreMouse: true => ignore mouse events
  306. // ignoreAnimation: true => ignore animations
  307. // ignoreDimensions: true => does not try to resize canvas
  308. // ignoreClear: true => does not clear canvas
  309. // offsetX: int => draws at a x offset
  310. // offsetY: int => draws at a y offset
  311. // scaleWidth: int => scales horizontally to width
  312. // scaleHeight: int => scales vertically to height
  313. // renderCallback: function => will call the function after the first render is completed
  314. // forceRedraw: function => will call the function on every frame, if it returns true, will redraw
  315. this.canvg = function (target, s, opts) {
  316. // no parameters
  317. if (target == null && s == null && opts == null) {
  318. var svgTags = document.getElementsByTagName('svg');
  319. for (var i=0; i<svgTags.length; i++) {
  320. var svgTag = svgTags[i];
  321. var c = document.createElement('canvas');
  322. c.width = svgTag.clientWidth;
  323. c.height = svgTag.clientHeight;
  324. svgTag.parentNode.insertBefore(c, svgTag);
  325. svgTag.parentNode.removeChild(svgTag);
  326. var div = document.createElement('div');
  327. div.appendChild(svgTag);
  328. canvg(c, div.innerHTML);
  329. }
  330. return;
  331. }
  332. opts = opts || {};
  333. if (typeof target == 'string') {
  334. target = document.getElementById(target);
  335. }
  336. // reuse class per canvas
  337. var svg;
  338. if (target.svg == null) {
  339. svg = build();
  340. target.svg = svg;
  341. }
  342. else {
  343. svg = target.svg;
  344. svg.stop();
  345. }
  346. svg.opts = opts;
  347. var ctx = target.getContext('2d');
  348. if (typeof(s.documentElement) != 'undefined') {
  349. // load from xml doc
  350. svg.loadXmlDoc(ctx, s);
  351. }
  352. else if (s.substr(0,1) == '<') {
  353. // load from xml string
  354. svg.loadXml(ctx, s);
  355. }
  356. else {
  357. // load from url
  358. svg.load(ctx, s);
  359. }
  360. }
  361. function build() {
  362. var svg = { };
  363. svg.FRAMERATE = 30;
  364. svg.MAX_VIRTUAL_PIXELS = 30000;
  365. // globals
  366. svg.init = function(ctx) {
  367. svg.Definitions = {};
  368. svg.Styles = {};
  369. svg.Animations = [];
  370. svg.Images = [];
  371. svg.ctx = ctx;
  372. svg.ViewPort = new (function () {
  373. this.viewPorts = [];
  374. this.Clear = function() { this.viewPorts = []; }
  375. this.SetCurrent = function(width, height) { this.viewPorts.push({ width: width, height: height }); }
  376. this.RemoveCurrent = function() { this.viewPorts.pop(); }
  377. this.Current = function() { return this.viewPorts[this.viewPorts.length - 1]; }
  378. this.width = function() { return this.Current().width; }
  379. this.height = function() { return this.Current().height; }
  380. this.ComputeSize = function(d) {
  381. if (d != null && typeof(d) == 'number') return d;
  382. if (d == 'x') return this.width();
  383. if (d == 'y') return this.height();
  384. return Math.sqrt(Math.pow(this.width(), 2) + Math.pow(this.height(), 2)) / Math.sqrt(2);
  385. }
  386. });
  387. }
  388. svg.init();
  389. // images loaded
  390. svg.ImagesLoaded = function() {
  391. for (var i=0; i<svg.Images.length; i++) {
  392. if (!svg.Images[i].loaded) return false;
  393. }
  394. return true;
  395. }
  396. // trim
  397. svg.trim = function(s) { return s.replace(/^\s+|\s+$/g, ''); }
  398. // compress spaces
  399. svg.compressSpaces = function(s) { return s.replace(/[\s\r\t\n]+/gm,' '); }
  400. // ajax
  401. svg.ajax = function(url) {
  402. var AJAX;
  403. if(window.XMLHttpRequest){AJAX=new XMLHttpRequest();}
  404. else{AJAX=new ActiveXObject('Microsoft.XMLHTTP');}
  405. if(AJAX){
  406. AJAX.open('GET',url,false);
  407. AJAX.send(null);
  408. return AJAX.responseText;
  409. }
  410. return null;
  411. }
  412. // parse xml
  413. svg.parseXml = function(xml) {
  414. if (window.DOMParser)
  415. {
  416. var parser = new DOMParser();
  417. return parser.parseFromString(xml, 'text/xml');
  418. }
  419. else
  420. {
  421. xml = xml.replace(/<!DOCTYPE svg[^>]*>/, '');
  422. var xmlDoc = new ActiveXObject('Microsoft.XMLDOM');
  423. xmlDoc.async = 'false';
  424. xmlDoc.loadXML(xml);
  425. return xmlDoc;
  426. }
  427. }
  428. svg.Property = function(name, value) {
  429. this.name = name;
  430. this.value = value;
  431. this.hasValue = function() {
  432. return (this.value != null && this.value !== '');
  433. }
  434. // return the numerical value of the property
  435. this.numValue = function() {
  436. if (!this.hasValue()) return 0;
  437. var n = parseFloat(this.value);
  438. if ((this.value + '').match(/%$/)) {
  439. n = n / 100.0;
  440. }
  441. return n;
  442. }
  443. this.valueOrDefault = function(def) {
  444. if (this.hasValue()) return this.value;
  445. return def;
  446. }
  447. this.numValueOrDefault = function(def) {
  448. if (this.hasValue()) return this.numValue();
  449. return def;
  450. }
  451. /* EXTENSIONS */
  452. var that = this;
  453. // color extensions
  454. this.Color = {
  455. // augment the current color value with the opacity
  456. addOpacity: function(opacity) {
  457. var newValue = that.value;
  458. if (opacity != null && opacity != '') {
  459. var color = new RGBColor(that.value);
  460. if (color.ok) {
  461. newValue = 'rgba(' + color.r + ', ' + color.g + ', ' + color.b + ', ' + opacity + ')';
  462. }
  463. }
  464. return new svg.Property(that.name, newValue);
  465. }
  466. }
  467. // definition extensions
  468. this.Definition = {
  469. // get the definition from the definitions table
  470. getDefinition: function() {
  471. var name = that.value.replace(/^(url\()?#([^\)]+)\)?$/, '$2');
  472. return svg.Definitions[name];
  473. },
  474. isUrl: function() {
  475. return that.value.indexOf('url(') == 0
  476. },
  477. getFillStyle: function(e) {
  478. var def = this.getDefinition();
  479. // gradient
  480. if (def != null && def.createGradient) {
  481. return def.createGradient(svg.ctx, e);
  482. }
  483. // pattern
  484. if (def != null && def.createPattern) {
  485. return def.createPattern(svg.ctx, e);
  486. }
  487. return null;
  488. }
  489. }
  490. // length extensions
  491. this.Length = {
  492. DPI: function(viewPort) {
  493. return 96.0; // TODO: compute?
  494. },
  495. EM: function(viewPort) {
  496. var em = 12;
  497. var fontSize = new svg.Property('fontSize', svg.Font.Parse(svg.ctx.font).fontSize);
  498. if (fontSize.hasValue()) em = fontSize.Length.toPixels(viewPort);
  499. return em;
  500. },
  501. // get the length as pixels
  502. toPixels: function(viewPort) {
  503. if (!that.hasValue()) return 0;
  504. var s = that.value+'';
  505. if (s.match(/em$/)) return that.numValue() * this.EM(viewPort);
  506. if (s.match(/ex$/)) return that.numValue() * this.EM(viewPort) / 2.0;
  507. if (s.match(/px$/)) return that.numValue();
  508. if (s.match(/pt$/)) return that.numValue() * 1.25;
  509. if (s.match(/pc$/)) return that.numValue() * 15;
  510. if (s.match(/cm$/)) return that.numValue() * this.DPI(viewPort) / 2.54;
  511. if (s.match(/mm$/)) return that.numValue() * this.DPI(viewPort) / 25.4;
  512. if (s.match(/in$/)) return that.numValue() * this.DPI(viewPort);
  513. if (s.match(/%$/)) return that.numValue() * svg.ViewPort.ComputeSize(viewPort);
  514. return that.numValue();
  515. }
  516. }
  517. // time extensions
  518. this.Time = {
  519. // get the time as milliseconds
  520. toMilliseconds: function() {
  521. if (!that.hasValue()) return 0;
  522. var s = that.value+'';
  523. if (s.match(/s$/)) return that.numValue() * 1000;
  524. if (s.match(/ms$/)) return that.numValue();
  525. return that.numValue();
  526. }
  527. }
  528. // angle extensions
  529. this.Angle = {
  530. // get the angle as radians
  531. toRadians: function() {
  532. if (!that.hasValue()) return 0;
  533. var s = that.value+'';
  534. if (s.match(/deg$/)) return that.numValue() * (Math.PI / 180.0);
  535. if (s.match(/grad$/)) return that.numValue() * (Math.PI / 200.0);
  536. if (s.match(/rad$/)) return that.numValue();
  537. return that.numValue() * (Math.PI / 180.0);
  538. }
  539. }
  540. }
  541. // fonts
  542. svg.Font = new (function() {
  543. this.Styles = ['normal','italic','oblique','inherit'];
  544. this.Variants = ['normal','small-caps','inherit'];
  545. this.Weights = ['normal','bold','bolder','lighter','100','200','300','400','500','600','700','800','900','inherit'];
  546. this.CreateFont = function(fontStyle, fontVariant, fontWeight, fontSize, fontFamily, inherit) {
  547. var f = inherit != null ? this.Parse(inherit) : this.CreateFont('', '', '', '', '', svg.ctx.font);
  548. return {
  549. fontFamily: fontFamily || f.fontFamily,
  550. fontSize: fontSize || f.fontSize,
  551. fontStyle: fontStyle || f.fontStyle,
  552. fontWeight: fontWeight || f.fontWeight,
  553. fontVariant: fontVariant || f.fontVariant,
  554. toString: function () { return [this.fontStyle, this.fontVariant, this.fontWeight, this.fontSize, this.fontFamily].join(' ') }
  555. }
  556. }
  557. var that = this;
  558. this.Parse = function(s) {
  559. var f = {};
  560. var d = svg.trim(svg.compressSpaces(s || '')).split(' ');
  561. var set = { fontSize: false, fontStyle: false, fontWeight: false, fontVariant: false }
  562. var ff = '';
  563. for (var i=0; i<d.length; i++) {
  564. if (!set.fontStyle && that.Styles.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontStyle = d[i]; set.fontStyle = true; }
  565. else if (!set.fontVariant && that.Variants.indexOf(d[i]) != -1) { if (d[i] != 'inherit') f.fontVariant = d[i]; set.fontStyle = set.fontVariant = true; }
  566. 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; }
  567. else if (!set.fontSize) { if (d[i] != 'inherit') f.fontSize = d[i].split('/')[0]; set.fontStyle = set.fontVariant = set.fontWeight = set.fontSize = true; }
  568. else { if (d[i] != 'inherit') ff += d[i]; }
  569. } if (ff != '') f.fontFamily = ff;
  570. return f;
  571. }
  572. });
  573. // points and paths
  574. svg.ToNumberArray = function(s) {
  575. var a = svg.trim(svg.compressSpaces((s || '').replace(/,/g, ' '))).split(' ');
  576. for (var i=0; i<a.length; i++) {
  577. a[i] = parseFloat(a[i]);
  578. }
  579. return a;
  580. }
  581. svg.Point = function(x, y) {
  582. this.x = x;
  583. this.y = y;
  584. this.angleTo = function(p) {
  585. return Math.atan2(p.y - this.y, p.x - this.x);
  586. }
  587. this.applyTransform = function(v) {
  588. var xp = this.x * v[0] + this.y * v[2] + v[4];
  589. var yp = this.x * v[1] + this.y * v[3] + v[5];
  590. this.x = xp;
  591. this.y = yp;
  592. }
  593. }
  594. svg.CreatePoint = function(s) {
  595. var a = svg.ToNumberArray(s);
  596. return new svg.Point(a[0], a[1]);
  597. }
  598. svg.CreatePath = function(s) {
  599. var a = svg.ToNumberArray(s);
  600. var path = [];
  601. for (var i=0; i<a.length; i+=2) {
  602. path.push(new svg.Point(a[i], a[i+1]));
  603. }
  604. return path;
  605. }
  606. // bounding box
  607. svg.BoundingBox = function(x1, y1, x2, y2) { // pass in initial points if you want
  608. this.x1 = Number.NaN;
  609. this.y1 = Number.NaN;
  610. this.x2 = Number.NaN;
  611. this.y2 = Number.NaN;
  612. this.x = function() { return this.x1; }
  613. this.y = function() { return this.y1; }
  614. this.width = function() { return this.x2 - this.x1; }
  615. this.height = function() { return this.y2 - this.y1; }
  616. this.addPoint = function(x, y) {
  617. if (x != null) {
  618. if (isNaN(this.x1) || isNaN(this.x2)) {
  619. this.x1 = x;
  620. this.x2 = x;
  621. }
  622. if (x < this.x1) this.x1 = x;
  623. if (x > this.x2) this.x2 = x;
  624. }
  625. if (y != null) {
  626. if (isNaN(this.y1) || isNaN(this.y2)) {
  627. this.y1 = y;
  628. this.y2 = y;
  629. }
  630. if (y < this.y1) this.y1 = y;
  631. if (y > this.y2) this.y2 = y;
  632. }
  633. }
  634. this.addX = function(x) { this.addPoint(x, null); }
  635. this.addY = function(y) { this.addPoint(null, y); }
  636. this.addBoundingBox = function(bb) {
  637. this.addPoint(bb.x1, bb.y1);
  638. this.addPoint(bb.x2, bb.y2);
  639. }
  640. this.addQuadraticCurve = function(p0x, p0y, p1x, p1y, p2x, p2y) {
  641. var cp1x = p0x + 2/3 * (p1x - p0x); // CP1 = QP0 + 2/3 *(QP1-QP0)
  642. var cp1y = p0y + 2/3 * (p1y - p0y); // CP1 = QP0 + 2/3 *(QP1-QP0)
  643. var cp2x = cp1x + 1/3 * (p2x - p0x); // CP2 = CP1 + 1/3 *(QP2-QP0)
  644. var cp2y = cp1y + 1/3 * (p2y - p0y); // CP2 = CP1 + 1/3 *(QP2-QP0)
  645. this.addBezierCurve(p0x, p0y, cp1x, cp2x, cp1y, cp2y, p2x, p2y);
  646. }
  647. this.addBezierCurve = function(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) {
  648. // from http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
  649. var p0 = [p0x, p0y], p1 = [p1x, p1y], p2 = [p2x, p2y], p3 = [p3x, p3y];
  650. this.addPoint(p0[0], p0[1]);
  651. this.addPoint(p3[0], p3[1]);
  652. for (i=0; i<=1; i++) {
  653. var f = function(t) {
  654. return Math.pow(1-t, 3) * p0[i]
  655. + 3 * Math.pow(1-t, 2) * t * p1[i]
  656. + 3 * (1-t) * Math.pow(t, 2) * p2[i]
  657. + Math.pow(t, 3) * p3[i];
  658. }
  659. var b = 6 * p0[i] - 12 * p1[i] + 6 * p2[i];
  660. var a = -3 * p0[i] + 9 * p1[i] - 9 * p2[i] + 3 * p3[i];
  661. var c = 3 * p1[i] - 3 * p0[i];
  662. if (a == 0) {
  663. if (b == 0) continue;
  664. var t = -c / b;
  665. if (0 < t && t < 1) {
  666. if (i == 0) this.addX(f(t));
  667. if (i == 1) this.addY(f(t));
  668. }
  669. continue;
  670. }
  671. var b2ac = Math.pow(b, 2) - 4 * c * a;
  672. if (b2ac < 0) continue;
  673. var t1 = (-b + Math.sqrt(b2ac)) / (2 * a);
  674. if (0 < t1 && t1 < 1) {
  675. if (i == 0) this.addX(f(t1));
  676. if (i == 1) this.addY(f(t1));
  677. }
  678. var t2 = (-b - Math.sqrt(b2ac)) / (2 * a);
  679. if (0 < t2 && t2 < 1) {
  680. if (i == 0) this.addX(f(t2));
  681. if (i == 1) this.addY(f(t2));
  682. }
  683. }
  684. }
  685. this.isPointInBox = function(x, y) {
  686. return (this.x1 <= x && x <= this.x2 && this.y1 <= y && y <= this.y2);
  687. }
  688. this.addPoint(x1, y1);
  689. this.addPoint(x2, y2);
  690. }
  691. // transforms
  692. svg.Transform = function(v) {
  693. var that = this;
  694. this.Type = {}
  695. // translate
  696. this.Type.translate = function(s) {
  697. this.p = svg.CreatePoint(s);
  698. this.apply = function(ctx) {
  699. ctx.translate(this.p.x || 0.0, this.p.y || 0.0);
  700. }
  701. this.applyToPoint = function(p) {
  702. p.applyTransform([1, 0, 0, 1, this.p.x || 0.0, this.p.y || 0.0]);
  703. }
  704. }
  705. // rotate
  706. this.Type.rotate = function(s) {
  707. var a = svg.ToNumberArray(s);
  708. this.angle = new svg.Property('angle', a[0]);
  709. this.cx = a[1] || 0;
  710. this.cy = a[2] || 0;
  711. this.apply = function(ctx) {
  712. ctx.translate(this.cx, this.cy);
  713. ctx.rotate(this.angle.Angle.toRadians());
  714. ctx.translate(-this.cx, -this.cy);
  715. }
  716. this.applyToPoint = function(p) {
  717. var a = this.angle.Angle.toRadians();
  718. p.applyTransform([1, 0, 0, 1, this.p.x || 0.0, this.p.y || 0.0]);
  719. p.applyTransform([Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 0, 0]);
  720. p.applyTransform([1, 0, 0, 1, -this.p.x || 0.0, -this.p.y || 0.0]);
  721. }
  722. }
  723. this.Type.scale = function(s) {
  724. this.p = svg.CreatePoint(s);
  725. this.apply = function(ctx) {
  726. ctx.scale(this.p.x || 1.0, this.p.y || this.p.x || 1.0);
  727. }
  728. this.applyToPoint = function(p) {
  729. p.applyTransform([this.p.x || 0.0, 0, 0, this.p.y || 0.0, 0, 0]);
  730. }
  731. }
  732. this.Type.matrix = function(s) {
  733. this.m = svg.ToNumberArray(s);
  734. this.apply = function(ctx) {
  735. ctx.transform(this.m[0], this.m[1], this.m[2], this.m[3], this.m[4], this.m[5]);
  736. }
  737. this.applyToPoint = function(p) {
  738. p.applyTransform(this.m);
  739. }
  740. }
  741. this.Type.SkewBase = function(s) {
  742. this.base = that.Type.matrix;
  743. this.base(s);
  744. this.angle = new svg.Property('angle', s);
  745. }
  746. this.Type.SkewBase.prototype = new this.Type.matrix;
  747. this.Type.skewX = function(s) {
  748. this.base = that.Type.SkewBase;
  749. this.base(s);
  750. this.m = [1, 0, Math.tan(this.angle.Angle.toRadians()), 1, 0, 0];
  751. }
  752. this.Type.skewX.prototype = new this.Type.SkewBase;
  753. this.Type.skewY = function(s) {
  754. this.base = that.Type.SkewBase;
  755. this.base(s);
  756. this.m = [1, Math.tan(this.angle.Angle.toRadians()), 0, 1, 0, 0];
  757. }
  758. this.Type.skewY.prototype = new this.Type.SkewBase;
  759. this.transforms = [];
  760. this.apply = function(ctx) {
  761. for (var i=0; i<this.transforms.length; i++) {
  762. this.transforms[i].apply(ctx);
  763. }
  764. }
  765. this.applyToPoint = function(p) {
  766. for (var i=0; i<this.transforms.length; i++) {
  767. this.transforms[i].applyToPoint(p);
  768. }
  769. }
  770. var data = svg.trim(svg.compressSpaces(v)).split(/\s(?=[a-z])/);
  771. for (var i=0; i<data.length; i++) {
  772. var type = data[i].split('(')[0];
  773. var s = data[i].split('(')[1].replace(')','');
  774. var transform = new this.Type[type](s);
  775. this.transforms.push(transform);
  776. }
  777. }
  778. // aspect ratio
  779. svg.AspectRatio = function(ctx, aspectRatio, width, desiredWidth, height, desiredHeight, minX, minY, refX, refY) {
  780. // aspect ratio - http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute
  781. aspectRatio = svg.compressSpaces(aspectRatio);
  782. aspectRatio = aspectRatio.replace(/^defer\s/,''); // ignore defer
  783. var align = aspectRatio.split(' ')[0] || 'xMidYMid';
  784. var meetOrSlice = aspectRatio.split(' ')[1] || 'meet';
  785. // calculate scale
  786. var scaleX = width / desiredWidth;
  787. var scaleY = height / desiredHeight;
  788. var scaleMin = Math.min(scaleX, scaleY);
  789. var scaleMax = Math.max(scaleX, scaleY);
  790. if (meetOrSlice == 'meet') { desiredWidth *= scaleMin; desiredHeight *= scaleMin; }
  791. if (meetOrSlice == 'slice') { desiredWidth *= scaleMax; desiredHeight *= scaleMax; }
  792. refX = new svg.Property('refX', refX);
  793. refY = new svg.Property('refY', refY);
  794. if (refX.hasValue() && refY.hasValue()) {
  795. ctx.translate(-scaleMin * refX.Length.toPixels('x'), -scaleMin * refY.Length.toPixels('y'));
  796. }
  797. else {
  798. // align
  799. if (align.match(/^xMid/) && ((meetOrSlice == 'meet' && scaleMin == scaleY) || (meetOrSlice == 'slice' && scaleMax == scaleY))) ctx.translate(width / 2.0 - desiredWidth / 2.0, 0);
  800. if (align.match(/YMid$/) && ((meetOrSlice == 'meet' && scaleMin == scaleX) || (meetOrSlice == 'slice' && scaleMax == scaleX))) ctx.translate(0, height / 2.0 - desiredHeight / 2.0);
  801. if (align.match(/^xMax/) && ((meetOrSlice == 'meet' && scaleMin == scaleY) || (meetOrSlice == 'slice' && scaleMax == scaleY))) ctx.translate(width - desiredWidth, 0);
  802. if (align.match(/YMax$/) && ((meetOrSlice == 'meet' && scaleMin == scaleX) || (meetOrSlice == 'slice' && scaleMax == scaleX))) ctx.translate(0, height - desiredHeight);
  803. }
  804. // scale
  805. if (align == 'none') ctx.scale(scaleX, scaleY);
  806. else if (meetOrSlice == 'meet') ctx.scale(scaleMin, scaleMin);
  807. else if (meetOrSlice == 'slice') ctx.scale(scaleMax, scaleMax);
  808. // translate
  809. ctx.translate(minX == null ? 0 : -minX, minY == null ? 0 : -minY);
  810. }
  811. // elements
  812. svg.Element = {}
  813. svg.Element.ElementBase = function(node) {
  814. this.attributes = {};
  815. this.styles = {};
  816. this.children = [];
  817. // get or create attribute
  818. this.attribute = function(name, createIfNotExists) {
  819. var a = this.attributes[name];
  820. if (a != null) return a;
  821. a = new svg.Property(name, '');
  822. if (createIfNotExists == true) this.attributes[name] = a;
  823. return a;
  824. }
  825. // get or create style, crawls up node tree
  826. this.style = function(name, createIfNotExists) {
  827. var s = this.styles[name];
  828. if (s != null) return s;
  829. var a = this.attribute(name);
  830. if (a != null && a.hasValue()) {
  831. return a;
  832. }
  833. var p = this.parent;
  834. if (p != null) {
  835. var ps = p.style(name);
  836. if (ps != null && ps.hasValue()) {
  837. return ps;
  838. }
  839. }
  840. s = new svg.Property(name, '');
  841. if (createIfNotExists == true) this.styles[name] = s;
  842. return s;
  843. }
  844. // base render
  845. this.render = function(ctx) {
  846. // don't render display=none
  847. if (this.style('display').value == 'none') return;
  848. // don't render visibility=hidden
  849. if (this.attribute('visibility').value == 'hidden') return;
  850. ctx.save();
  851. this.setContext(ctx);
  852. // mask
  853. if (this.attribute('mask').hasValue()) {
  854. var mask = this.attribute('mask').Definition.getDefinition();
  855. if (mask != null) mask.apply(ctx, this);
  856. }
  857. else if (this.style('filter').hasValue()) {
  858. var filter = this.style('filter').Definition.getDefinition();
  859. if (filter != null) filter.apply(ctx, this);
  860. }
  861. else this.renderChildren(ctx);
  862. this.clearContext(ctx);
  863. ctx.restore();
  864. }
  865. // base set context
  866. this.setContext = function(ctx) {
  867. // OVERRIDE ME!
  868. }
  869. // base clear context
  870. this.clearContext = function(ctx) {
  871. // OVERRIDE ME!
  872. }
  873. // base render children
  874. this.renderChildren = function(ctx) {
  875. for (var i=0; i<this.children.length; i++) {
  876. this.children[i].render(ctx);
  877. }
  878. }
  879. this.addChild = function(childNode, create) {
  880. var child = childNode;
  881. if (create) child = svg.CreateElement(childNode);
  882. child.parent = this;
  883. this.children.push(child);
  884. }
  885. if (node != null && node.nodeType == 1) { //ELEMENT_NODE
  886. // add children
  887. for (var i=0; i<node.childNodes.length; i++) {
  888. var childNode = node.childNodes[i];
  889. if (childNode.nodeType == 1) this.addChild(childNode, true); //ELEMENT_NODE
  890. }
  891. // add attributes
  892. for (var i=0; i<node.attributes.length; i++) {
  893. var attribute = node.attributes[i];
  894. this.attributes[attribute.nodeName] = new svg.Property(attribute.nodeName, attribute.nodeValue);
  895. }
  896. // add tag styles
  897. var styles = svg.Styles[node.nodeName];
  898. if (styles != null) {
  899. for (var name in styles) {
  900. this.styles[name] = styles[name];
  901. }
  902. }
  903. // add class styles
  904. if (this.attribute('class').hasValue()) {
  905. var classes = svg.compressSpaces(this.attribute('class').value).split(' ');
  906. for (var j=0; j<classes.length; j++) {
  907. styles = svg.Styles['.'+classes[j]];
  908. if (styles != null) {
  909. for (var name in styles) {
  910. this.styles[name] = styles[name];
  911. }
  912. }
  913. styles = svg.Styles[node.nodeName+'.'+classes[j]];
  914. if (styles != null) {
  915. for (var name in styles) {
  916. this.styles[name] = styles[name];
  917. }
  918. }
  919. }
  920. }
  921. // add inline styles
  922. if (this.attribute('style').hasValue()) {
  923. var styles = this.attribute('style').value.split(';');
  924. for (var i=0; i<styles.length; i++) {
  925. if (svg.trim(styles[i]) != '') {
  926. var style = styles[i].split(':');
  927. var name = svg.trim(style[0]);
  928. var value = svg.trim(style[1]);
  929. this.styles[name] = new svg.Property(name, value);
  930. }
  931. }
  932. }
  933. // add id
  934. if (this.attribute('id').hasValue()) {
  935. if (svg.Definitions[this.attribute('id').value] == null) {
  936. svg.Definitions[this.attribute('id').value] = this;
  937. }
  938. }
  939. }
  940. }
  941. svg.Element.RenderedElementBase = function(node) {
  942. this.base = svg.Element.ElementBase;
  943. this.base(node);
  944. this.setContext = function(ctx) {
  945. // fill
  946. if (this.style('fill').Definition.isUrl()) {
  947. var fs = this.style('fill').Definition.getFillStyle(this);
  948. if (fs != null) ctx.fillStyle = fs;
  949. }
  950. else if (this.style('fill').hasValue()) {
  951. var fillStyle = this.style('fill');
  952. if (this.style('fill-opacity').hasValue()) fillStyle = fillStyle.Color.addOpacity(this.style('fill-opacity').value);
  953. ctx.fillStyle = (fillStyle.value == 'none' ? 'rgba(0,0,0,0)' : fillStyle.value);
  954. }
  955. // stroke
  956. if (this.style('stroke').Definition.isUrl()) {
  957. var fs = this.style('stroke').Definition.getFillStyle(this);
  958. if (fs != null) ctx.strokeStyle = fs;
  959. }
  960. else if (this.style('stroke').hasValue()) {
  961. var strokeStyle = this.style('stroke');
  962. if (this.style('stroke-opacity').hasValue()) strokeStyle = strokeStyle.Color.addOpacity(this.style('stroke-opacity').value);
  963. ctx.strokeStyle = (strokeStyle.value == 'none' ? 'rgba(0,0,0,0)' : strokeStyle.value);
  964. }
  965. if (this.style('stroke-width').hasValue()) ctx.lineWidth = this.style('stroke-width').Length.toPixels();
  966. if (this.style('stroke-linecap').hasValue()) ctx.lineCap = this.style('stroke-linecap').value;
  967. if (this.style('stroke-linejoin').hasValue()) ctx.lineJoin = this.style('stroke-linejoin').value;
  968. if (this.style('stroke-miterlimit').hasValue()) ctx.miterLimit = this.style('stroke-miterlimit').value;
  969. // font
  970. if (typeof(ctx.font) != 'undefined') {
  971. ctx.font = svg.Font.CreateFont(
  972. this.style('font-style').value,
  973. this.style('font-variant').value,
  974. this.style('font-weight').value,
  975. this.style('font-size').hasValue() ? this.style('font-size').Length.toPixels() + 'px' : '',
  976. this.style('font-family').value).toString();
  977. }
  978. // transform
  979. if (this.attribute('transform').hasValue()) {
  980. var transform = new svg.Transform(this.attribute('transform').value);
  981. transform.apply(ctx);
  982. }
  983. // clip
  984. if (this.attribute('clip-path').hasValue()) {
  985. var clip = this.attribute('clip-path').Definition.getDefinition();
  986. if (clip != null) clip.apply(ctx);
  987. }
  988. // opacity
  989. if (this.style('opacity').hasValue()) {
  990. ctx.globalAlpha = this.style('opacity').numValue();
  991. }
  992. }
  993. }
  994. svg.Element.RenderedElementBase.prototype = new svg.Element.ElementBase;
  995. svg.Element.PathElementBase = function(node) {
  996. this.base = svg.Element.RenderedElementBase;
  997. this.base(node);
  998. this.path = function(ctx) {
  999. if (ctx != null) ctx.beginPath();
  1000. return new svg.BoundingBox();
  1001. }
  1002. this.renderChildren = function(ctx) {
  1003. this.path(ctx);
  1004. svg.Mouse.checkPath(this, ctx);
  1005. if (ctx.fillStyle != '') ctx.fill();
  1006. if (ctx.strokeStyle != '') ctx.stroke();
  1007. var markers = this.getMarkers();
  1008. if (markers != null) {
  1009. if (this.style('marker-start').Definition.isUrl()) {
  1010. var marker = this.style('marker-start').Definition.getDefinition();
  1011. marker.render(ctx, markers[0][0], markers[0][1]);
  1012. }
  1013. if (this.style('marker-mid').Definition.isUrl()) {
  1014. var marker = this.style('marker-mid').Definition.getDefinition();
  1015. for (var i=1;i<markers.length-1;i++) {
  1016. marker.render(ctx, markers[i][0], markers[i][1]);
  1017. }
  1018. }
  1019. if (this.style('marker-end').Definition.isUrl()) {
  1020. var marker = this.style('marker-end').Definition.getDefinition();
  1021. marker.render(ctx, markers[markers.length-1][0], markers[markers.length-1][1]);
  1022. }
  1023. }
  1024. }
  1025. this.getBoundingBox = function() {
  1026. return this.path();
  1027. }
  1028. this.getMarkers = function() {
  1029. return null;
  1030. }
  1031. }
  1032. svg.Element.PathElementBase.prototype = new svg.Element.RenderedElementBase;
  1033. // svg element
  1034. svg.Element.svg = function(node) {
  1035. this.base = svg.Element.RenderedElementBase;
  1036. this.base(node);
  1037. this.baseClearContext = this.clearContext;
  1038. this.clearContext = function(ctx) {
  1039. this.baseClearContext(ctx);
  1040. svg.ViewPort.RemoveCurrent();
  1041. }
  1042. this.baseSetContext = this.setContext;
  1043. this.setContext = function(ctx) {
  1044. // initial values
  1045. ctx.strokeStyle = 'rgba(0,0,0,0)';
  1046. ctx.lineCap = 'butt';
  1047. ctx.lineJoin = 'miter';
  1048. ctx.miterLimit = 4;
  1049. this.baseSetContext(ctx);
  1050. // create new view port
  1051. if (this.attribute('x').hasValue() && this.attribute('y').hasValue()) {
  1052. ctx.translate(this.attribute('x').Length.toPixels('x'), this.attribute('y').Length.toPixels('y'));
  1053. }
  1054. var width = svg.ViewPort.width();
  1055. var height = svg.ViewPort.height();
  1056. if (typeof(this.root) == 'undefined' && this.attribute('width').hasValue() && this.attribute('height').hasValue()) {
  1057. width = this.attribute('width').Length.toPixels('x');
  1058. height = this.attribute('height').Length.toPixels('y');
  1059. var x = 0;
  1060. var y = 0;
  1061. if (this.attribute('refX').hasValue() && this.attribute('refY').hasValue()) {
  1062. x = -this.attribute('refX').Length.toPixels('x');
  1063. y = -this.attribute('refY').Length.toPixels('y');
  1064. }
  1065. ctx.beginPath();
  1066. ctx.moveTo(x, y);
  1067. ctx.lineTo(width, y);
  1068. ctx.lineTo(width, height);
  1069. ctx.lineTo(x, height);
  1070. ctx.closePath();
  1071. ctx.clip();
  1072. }
  1073. svg.ViewPort.SetCurrent(width, height);
  1074. // viewbox
  1075. if (this.attribute('viewBox').hasValue()) {
  1076. var viewBox = svg.ToNumberArray(this.attribute('viewBox').value);
  1077. var minX = viewBox[0];
  1078. var minY = viewBox[1];
  1079. width = viewBox[2];
  1080. height = viewBox[3];
  1081. svg.AspectRatio(ctx,
  1082. this.attribute('preserveAspectRatio').value,
  1083. svg.ViewPort.width(),
  1084. width,
  1085. svg.ViewPort.height(),
  1086. height,
  1087. minX,
  1088. minY,
  1089. this.attribute('refX').value,
  1090. this.attribute('refY').value);
  1091. svg.ViewPort.RemoveCurrent();
  1092. svg.ViewPort.SetCurrent(viewBox[2], viewBox[3]);
  1093. }
  1094. }
  1095. }
  1096. svg.Element.svg.prototype = new svg.Element.RenderedElementBase;
  1097. // rect element
  1098. svg.Element.rect = function(node) {
  1099. this.base = svg.Element.PathElementBase;
  1100. this.base(node);
  1101. this.path = function(ctx) {
  1102. var x = this.attribute('x').Length.toPixels('x');
  1103. var y = this.attribute('y').Length.toPixels('y');
  1104. var width = this.attribute('width').Length.toPixels('x');
  1105. var height = this.attribute('height').Length.toPixels('y');
  1106. var rx = this.attribute('rx').Length.toPixels('x');
  1107. var ry = this.attribute('ry').Length.toPixels('y');
  1108. if (this.attribute('rx').hasValue() && !this.attribute('ry').hasValue()) ry = rx;
  1109. if (this.attribute('ry').hasValue() && !this.attribute('rx').hasValue()) rx = ry;
  1110. if (ctx != null) {
  1111. ctx.beginPath();
  1112. ctx.moveTo(x + rx, y);
  1113. ctx.lineTo(x + width - rx, y);
  1114. ctx.quadraticCurveTo(x + width, y, x + width, y + ry)
  1115. ctx.lineTo(x + width, y + height - ry);
  1116. ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height)
  1117. ctx.lineTo(x + rx, y + height);
  1118. ctx.quadraticCurveTo(x, y + height, x, y + height - ry)
  1119. ctx.lineTo(x, y + ry);
  1120. ctx.quadraticCurveTo(x, y, x + rx, y)
  1121. ctx.closePath();
  1122. }
  1123. return new svg.BoundingBox(x, y, x + width, y + height);
  1124. }
  1125. }
  1126. svg.Element.rect.prototype = new svg.Element.PathElementBase;
  1127. // circle element
  1128. svg.Element.circle = function(node) {
  1129. this.base = svg.Element.PathElementBase;
  1130. this.base(node);
  1131. this.path = function(ctx) {
  1132. var cx = this.attribute('cx').Length.toPixels('x');
  1133. var cy = this.attribute('cy').Length.toPixels('y');
  1134. var r = this.attribute('r').Length.toPixels();
  1135. if (ctx != null) {
  1136. ctx.beginPath();
  1137. ctx.arc(cx, cy, r, 0, Math.PI * 2, true);
  1138. ctx.closePath();
  1139. }
  1140. return new svg.BoundingBox(cx - r, cy - r, cx + r, cy + r);
  1141. }
  1142. }
  1143. svg.Element.circle.prototype = new svg.Element.PathElementBase;
  1144. // ellipse element
  1145. svg.Element.ellipse = function(node) {
  1146. this.base = svg.Element.PathElementBase;
  1147. this.base(node);
  1148. this.path = function(ctx) {
  1149. var KAPPA = 4 * ((Math.sqrt(2) - 1) / 3);
  1150. var rx = this.attribute('rx').Length.toPixels('x');
  1151. var ry = this.attribute('ry').Length.toPixels('y');
  1152. var cx = this.attribute('cx').Length.toPixels('x');
  1153. var cy = this.attribute('cy').Length.toPixels('y');
  1154. if (ctx != null) {
  1155. ctx.beginPath();
  1156. ctx.moveTo(cx, cy - ry);
  1157. ctx.bezierCurveTo(cx + (KAPPA * rx), cy - ry, cx + rx, cy - (KAPPA * ry), cx + rx, cy);
  1158. ctx.bezierCurveTo(cx + rx, cy + (KAPPA * ry), cx + (KAPPA * rx), cy + ry, cx, cy + ry);
  1159. ctx.bezierCurveTo(cx - (KAPPA * rx), cy + ry, cx - rx, cy + (KAPPA * ry), cx - rx, cy);
  1160. ctx.bezierCurveTo(cx - rx, cy - (KAPPA * ry), cx - (KAPPA * rx), cy - ry, cx, cy - ry);
  1161. ctx.closePath();
  1162. }
  1163. return new svg.BoundingBox(cx - rx, cy - ry, cx + rx, cy + ry);
  1164. }
  1165. }
  1166. svg.Element.ellipse.prototype = new svg.Element.PathElementBase;
  1167. // line element
  1168. svg.Element.line = function(node) {
  1169. this.base = svg.Element.PathElementBase;
  1170. this.base(node);
  1171. this.getPoints = function() {
  1172. return [
  1173. new svg.Point(this.attribute('x1').Length.toPixels('x'), this.attribute('y1').Length.toPixels('y')),
  1174. new svg.Point(this.attribute('x2').Length.toPixels('x'), this.attribute('y2').Length.toPixels('y'))];
  1175. }
  1176. this.path = function(ctx) {
  1177. var points = this.getPoints();
  1178. if (ctx != null) {
  1179. ctx.beginPath();
  1180. ctx.moveTo(points[0].x, points[0].y);
  1181. ctx.lineTo(points[1].x, points[1].y);
  1182. }
  1183. return new svg.BoundingBox(points[0].x, points[0].y, points[1].x, points[1].y);
  1184. }
  1185. this.getMarkers = function() {
  1186. var points = this.getPoints();
  1187. var a = points[0].angleTo(points[1]);
  1188. return [[points[0], a], [points[1], a]];
  1189. }
  1190. }
  1191. svg.Element.line.prototype = new svg.Element.PathElementBase;
  1192. // polyline element
  1193. svg.Element.polyline = function(node) {
  1194. this.base = svg.Element.PathElementBase;
  1195. this.base(node);
  1196. this.points = svg.CreatePath(this.attribute('points').value);
  1197. this.path = function(ctx) {
  1198. var bb = new svg.BoundingBox(this.points[0].x, this.points[0].y);
  1199. if (ctx != null) {
  1200. ctx.beginPath();
  1201. ctx.moveTo(this.points[0].x, this.points[0].y);
  1202. }
  1203. for (var i=1; i<this.points.length; i++) {
  1204. bb.addPoint(this.points[i].x, this.points[i].y);
  1205. if (ctx != null) ctx.lineTo(this.points[i].x, this.points[i].y);
  1206. }
  1207. return bb;
  1208. }
  1209. this.getMarkers = function() {
  1210. var markers = [];
  1211. for (var i=0; i<this.points.length - 1; i++) {
  1212. markers.push([this.points[i], this.points[i].angleTo(this.points[i+1])]);
  1213. }
  1214. markers.push([this.points[this.points.length-1], markers[markers.length-1][1]]);
  1215. return markers;
  1216. }
  1217. }
  1218. svg.Element.polyline.prototype = new svg.Element.PathElementBase;
  1219. // polygon element
  1220. svg.Element.polygon = function(node) {
  1221. this.base = svg.Element.polyline;
  1222. this.base(node);
  1223. this.basePath = this.path;
  1224. this.path = function(ctx) {
  1225. var bb = this.basePath(ctx);
  1226. if (ctx != null) {
  1227. ctx.lineTo(this.points[0].x, this.points[0].y);
  1228. ctx.closePath();
  1229. }
  1230. return bb;
  1231. }
  1232. }
  1233. svg.Element.polygon.prototype = new svg.Element.polyline;
  1234. // path element
  1235. svg.Element.path = function(node) {
  1236. this.base = svg.Element.PathElementBase;
  1237. this.base(node);
  1238. var d = this.attribute('d').value;
  1239. // TODO: convert to real lexer based on http://www.w3.org/TR/SVG11/paths.html#PathDataBNF
  1240. d = d.replace(/,/gm,' '); // get rid of all commas
  1241. d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from commands
  1242. d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from commands
  1243. d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([^\s])/gm,'$1 $2'); // separate commands from points
  1244. d = d.replace(/([^\s])([MmZzLlHhVvCcSsQqTtAa])/gm,'$1 $2'); // separate commands from points
  1245. d = d.replace(/([0-9])([+\-])/gm,'$1 $2'); // separate digits when no comma
  1246. d = d.replace(/(\.[0-9]*)(\.)/gm,'$1 $2'); // separate digits when no comma
  1247. d = d.replace(/([Aa](\s+[0-9]+){3})\s+([01])\s*([01])/gm,'$1 $3 $4 '); // shorthand elliptical arc path syntax
  1248. d = svg.compressSpaces(d); // compress multiple spaces
  1249. d = svg.trim(d);
  1250. this.PathParser = new (function(d) {
  1251. this.tokens = d.split(' ');
  1252. this.reset = function() {
  1253. this.i = -1;
  1254. this.command = '';
  1255. this.previousCommand = '';
  1256. this.start = new svg.Point(0, 0);
  1257. this.control = new svg.Point(0, 0);
  1258. this.current = new svg.Point(0, 0);
  1259. this.points = [];
  1260. this.angles = [];
  1261. }
  1262. this.isEnd = function() {
  1263. return this.i >= this.tokens.length - 1;
  1264. }
  1265. this.isCommandOrEnd = function() {
  1266. if (this.isEnd()) return true;
  1267. return this.tokens[this.i + 1].match(/^[A-Za-z]$/) != null;
  1268. }
  1269. this.isRelativeCommand = function() {
  1270. return this.command == this.command.toLowerCase();
  1271. }
  1272. this.getToken = function() {
  1273. this.i = this.i + 1;
  1274. return this.tokens[this.i];
  1275. }
  1276. this.getScalar = function() {
  1277. return parseFloat(this.getToken());
  1278. }
  1279. this.nextCommand = function() {
  1280. this.previousCommand = this.command;
  1281. this.command = this.getToken();
  1282. }
  1283. this.getPoint = function() {
  1284. var p = new svg.Point(this.getScalar(), this.getScalar());
  1285. return this.makeAbsolute(p);
  1286. }
  1287. this.getAsControlPoint = function() {
  1288. var p = this.getPoint();
  1289. this.control = p;
  1290. return p;
  1291. }
  1292. this.getAsCurrentPoint = function() {
  1293. var p = this.getPoint();
  1294. this.current = p;
  1295. return p;
  1296. }
  1297. this.getReflectedControlPoint = function() {
  1298. if (this.previousCommand.toLowerCase() != 'c' && this.previousCommand.toLowerCase() != 's') {
  1299. return this.current;
  1300. }
  1301. // reflect point
  1302. var p = new svg.Point(2 * this.current.x - this.control.x, 2 * this.current.y - this.control.y);
  1303. return p;
  1304. }
  1305. this.makeAbsolute = function(p) {
  1306. if (this.isRelativeCommand()) {
  1307. p.x = this.current.x + p.x;
  1308. p.y = this.current.y + p.y;
  1309. }
  1310. return p;
  1311. }
  1312. this.addMarker = function(p, from, priorTo) {
  1313. // if the last angle isn't filled in because we didn't have this point yet ...
  1314. if (priorTo != null && this.angles.length > 0 && this.angles[this.angles.length-1] == null) {
  1315. this.angles[this.angles.length-1] = this.points[this.points.length-1].angleTo(priorTo);
  1316. }
  1317. this.addMarkerAngle(p, from == null ? null : from.angleTo(p));
  1318. }
  1319. this.addMarkerAngle = function(p, a) {
  1320. this.points.push(p);
  1321. this.angles.push(a);
  1322. }
  1323. this.getMarkerPoints = function() { return this.points; }
  1324. this.getMarkerAngles = function() {
  1325. for (var i=0; i<this.angles.length; i++) {
  1326. if (this.angles[i] == null) {
  1327. for (var j=i+1; j<this.angles.length; j++) {
  1328. if (this.angles[j] != null) {
  1329. this.angles[i] = this.angles[j];
  1330. break;
  1331. }
  1332. }
  1333. }
  1334. }
  1335. return this.angles;
  1336. }
  1337. })(d);
  1338. this.path = function(ctx) {
  1339. var pp = this.PathParser;
  1340. pp.reset();
  1341. var bb = new svg.BoundingBox();
  1342. if (ctx != null) ctx.beginPath();
  1343. while (!pp.isEnd()) {
  1344. pp.nextCommand();
  1345. switch (pp.command.toUpperCase()) {
  1346. case 'M':
  1347. var p = pp.getAsCurrentPoint();
  1348. pp.addMarker(p);
  1349. bb.addPoint(p.x, p.y);
  1350. if (ctx != null) ctx.moveTo(p.x, p.y);
  1351. pp.start = pp.current;
  1352. while (!pp.isCommandOrEnd()) {
  1353. var p = pp.getAsCurrentPoint();
  1354. pp.addMarker(p, pp.start);
  1355. bb.addPoint(p.x, p.y);
  1356. if (ctx != null) ctx.lineTo(p.x, p.y);
  1357. }
  1358. break;
  1359. case 'L':
  1360. while (!pp.isCommandOrEnd()) {
  1361. var c = pp.current;
  1362. var p = pp.getAsCurrentPoint();
  1363. pp.addMarker(p, c);
  1364. bb.addPoint(p.x, p.y);
  1365. if (ctx != null) ctx.lineTo(p.x, p.y);
  1366. }
  1367. break;
  1368. case 'H':
  1369. while (!pp.isCommandOrEnd()) {
  1370. var newP = new svg.Point((pp.isRelativeCommand() ? pp.current.x : 0) + pp.getScalar(), pp.current.y);
  1371. pp.addMarker(newP, pp.current);
  1372. pp.current = newP;
  1373. bb.addPoint(pp.current.x, pp.current.y);
  1374. if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y);
  1375. }
  1376. break;
  1377. case 'V':
  1378. while (!pp.isCommandOrEnd()) {
  1379. var newP = new svg.Point(pp.current.x, (pp.isRelativeCommand() ? pp.current.y : 0) + pp.getScalar());
  1380. pp.addMarker(newP, pp.current);
  1381. pp.current = newP;
  1382. bb.addPoint(pp.current.x, pp.current.y);
  1383. if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y);
  1384. }
  1385. break;
  1386. case 'C':
  1387. while (!pp.isCommandOrEnd()) {
  1388. var curr = pp.current;
  1389. var p1 = pp.getPoint();
  1390. var cntrl = pp.getAsControlPoint();
  1391. var cp = pp.getAsCurrentPoint();
  1392. pp.addMarker(cp, cntrl, p1);
  1393. bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
  1394. if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
  1395. }
  1396. break;
  1397. case 'S':
  1398. while (!pp.isCommandOrEnd()) {
  1399. var curr = pp.current;
  1400. var p1 = pp.getReflectedControlPoint();
  1401. var cntrl = pp.getAsControlPoint();
  1402. var cp = pp.getAsCurrentPoint();
  1403. pp.addMarker(cp, cntrl, p1);
  1404. bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
  1405. if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y);
  1406. }
  1407. break;
  1408. case 'Q':
  1409. while (!pp.isCommandOrEnd()) {
  1410. var curr = pp.current;
  1411. var cntrl = pp.getAsControlPoint();
  1412. var cp = pp.getAs