mxStencil.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887
  1. /**
  2. * Copyright (c) 2006-2015, JGraph Ltd
  3. * Copyright (c) 2006-2015, Gaudenz Alder
  4. */
  5. /**
  6. * Class: mxStencil
  7. *
  8. * Implements a generic shape which is based on a XML node as a description.
  9. *
  10. * shape:
  11. *
  12. * The outer element is *shape*, that has attributes:
  13. *
  14. * - "name", string, required. The stencil name that uniquely identifies the shape.
  15. * - "w" and "h" are optional decimal view bounds. This defines your co-ordinate
  16. * system for the graphics operations in the shape. The default is 100,100.
  17. * - "aspect", optional string. Either "variable", the default, or "fixed". Fixed
  18. * means always render the shape with the aspect ratio defined by the ratio w/h.
  19. * Variable causes the ratio to match that of the geometry of the current vertex.
  20. * - "strokewidth", optional string. Either an integer or the string "inherit".
  21. * "inherit" indicates that the strokeWidth of the cell is only changed on scaling,
  22. * not on resizing. Default is "1".
  23. * If numeric values are used, the strokeWidth of the cell is changed on both
  24. * scaling and resizing and the value defines the multiple that is applied to
  25. * the width.
  26. *
  27. * connections:
  28. *
  29. * If you want to define specific fixed connection points on the shape use the
  30. * *connections* element. Each *constraint* element within connections defines
  31. * a fixed connection point on the shape. Constraints have attributes:
  32. *
  33. * - "perimeter", required. 1 or 0. 0 sets the connection point where specified
  34. * by x,y. 1 Causes the position of the connection point to be extrapolated from
  35. * the center of the shape, through x,y to the point of intersection with the
  36. * perimeter of the shape.
  37. * - "x" and "y" are the position of the fixed point relative to the bounds of
  38. * the shape. They can be automatically adjusted if perimeter=1. So, (0,0) is top
  39. * left, (0.5,0.5) the center, (1,0.5) the center of the right hand edge of the
  40. * bounds, etc. Values may be less than 0 or greater than 1 to be positioned
  41. * outside of the shape.
  42. * - "name", optional string. A unique identifier for the port on the shape.
  43. *
  44. * background and foreground:
  45. *
  46. * The path of the graphics drawing is split into two elements, *foreground* and
  47. * *background*. The split is to define which part any shadow applied to the shape
  48. * is derived from (the background). This, generally, means the background is the
  49. * line tracing of the outside of the shape, but not always.
  50. *
  51. * Any stroke, fill or fillstroke of a background must be the first element of the
  52. * foreground element, they must not be used within *background*. If the background
  53. * is empty, this is not required.
  54. *
  55. * Because the background cannot have any fill or stroke, it can contain only one
  56. * *path*, *rect*, *roundrect* or *ellipse* element (or none). It can also not
  57. * include *image*, *text* or *include-shape*.
  58. *
  59. * Note that the state, styling and drawing in mxGraph stencils is very close in
  60. * design to that of HTML 5 canvas. Tutorials on this subject, if you're not
  61. * familiar with the topic, will give a good high-level introduction to the
  62. * concepts used.
  63. *
  64. * State:
  65. *
  66. * Rendering within the foreground and background elements has the concept of
  67. * state. There are two types of operations other than state save/load, styling
  68. * and drawing. The styling operations change the current state, so you can save
  69. * the current state with <save/> and pull the last saved state from the state
  70. * stack using <restore/>.
  71. *
  72. * Styling:
  73. *
  74. * The elements that change colors within the current state all take a hash
  75. * prefixed hex color code (eg. "#FFEA80") or "fill" or "stroke" to reference
  76. * the current state.
  77. *
  78. * - *strokecolor*, this sets the color that drawing paths will be rendered in
  79. * when a stroke or fillstroke command is issued.
  80. * - *fillcolor*, this sets the color that the inside of closed paths will be
  81. * rendered in when a fill or fillstroke command is issued.
  82. * - *fontcolor*, this sets the color that fonts are rendered in when text is drawn.
  83. *
  84. * *alpha* defines the degree of transparency used between 1.0 for fully opaque
  85. * and 0.0 for fully transparent.
  86. *
  87. * *fillalpha* defines the degree of fill transparency used between 1.0 for fully
  88. * opaque and 0.0 for fully transparent.
  89. *
  90. * *strokealpha* defines the degree of stroke transparency used between 1.0 for
  91. * fully opaque and 0.0 for fully transparent.
  92. *
  93. * *strokewidth* defines the integer thickness of drawing elements rendered by
  94. * stroking. Use fixed="1" to apply the value as-is, without scaling.
  95. *
  96. * *dashed* is "1" for dashing enabled and "0" for disabled.
  97. *
  98. * When *dashed* is enabled the current dash pattern, defined by *dashpattern*,
  99. * is used on strokes. dashpattern is a sequence of space separated "on, off"
  100. * lengths that define what distance to paint the stroke for, then what distance
  101. * to paint nothing for, repeat... The default is "3 3". You could define a more
  102. * complex pattern with "5 3 2 6", for example. Generally, it makes sense to have
  103. * an even number of elements in the dashpattern, but that's not required.
  104. *
  105. * *linejoin*, *linecap* and *miterlimit* are best explained by the Mozilla page
  106. * on Canvas styling (about halfway down). The values are all the same except we
  107. * use "flat" for linecap, instead of Canvas' "butt".
  108. *
  109. * For font styling there are.
  110. *
  111. * - *fontsize*, an integer,
  112. * - *fontstyle*, an ORed bit pattern of bold (1), italic (2) and underline (4),
  113. * i.e bold underline is "5".
  114. * - *fontfamily*, is a string defining the typeface to be used.
  115. *
  116. * Drawing:
  117. *
  118. * Most drawing is contained within a *path* element. Again, the graphic
  119. * primitives are very similar to that of HTML 5 canvas.
  120. *
  121. * - *move* to attributes required decimals (x,y).
  122. * - *line* to attributes required decimals (x,y).
  123. * - *quad* to required decimals (x2,y2) via control point required decimals
  124. * (x1,y1).
  125. * - *curve* to required decimals (x3,y3), via control points required decimals
  126. * (x1,y1) and (x2,y2).
  127. * - *arc*, this doesn't follow the HTML Canvas signatures, instead it's a copy
  128. * of the SVG arc command. The SVG specification documentation gives the best
  129. * description of its behaviors. The attributes are named identically, they are
  130. * decimals and all required.
  131. * - *close* ends the current subpath and causes an automatic straight line to
  132. * be drawn from the current point to the initial point of the current subpath.
  133. *
  134. * Complex drawing:
  135. *
  136. * In addition to the graphics primitive operations there are non-primitive
  137. * operations. These provide an easy method to draw some basic shapes.
  138. *
  139. * - *rect*, attributes "x", "y", "w", "h", all required decimals
  140. * - *roundrect*, attributes "x", "y", "w", "h", all required decimals. Also
  141. * "arcsize" an optional decimal attribute defining how large, the corner curves
  142. * are.
  143. * - *ellipse*, attributes "x", "y", "w", "h", all required decimals.
  144. *
  145. * Note that these 3 shapes and all paths must be followed by either a fill,
  146. * stroke, or fillstroke.
  147. *
  148. * Text:
  149. *
  150. * *text* elements have the following attributes.
  151. *
  152. * - "str", the text string to display, required.
  153. * - "x" and "y", the decimal location (x,y) of the text element, required.
  154. * - "align", the horizontal alignment of the text element, either "left",
  155. * "center" or "right". Optional, default is "left".
  156. * - "valign", the vertical alignment of the text element, either "top", "middle"
  157. * or "bottom". Optional, default is "top".
  158. * - "localized", 0 or 1, if 1 then the "str" actually contains a key to use to
  159. * fetch the value out of mxResources. Optional, default is
  160. * <mxStencil.defaultLocalized>.
  161. * - "vertical", 0 or 1, if 1 the label is rendered vertically (rotated by 90
  162. * degrees). Optional, default is 0.
  163. * - "rotation", angle in degrees (0 to 360). The angle to rotate the text by.
  164. * Optional, default is 0.
  165. * - "align-shape", 0 or 1, if 0 ignore the rotation of the shape when setting
  166. * the text rotation. Optional, default is 1.
  167. *
  168. * If <allowEval> is true, then the text content of the this element can define
  169. * a function which is invoked with the shape as the only argument and returns
  170. * the value for the text element (ignored if the str attribute is not null).
  171. *
  172. * Images:
  173. *
  174. * *image* elements can either be external URLs, or data URIs, where supported
  175. * (not in IE 7-). Attributes are:
  176. *
  177. * - "src", required string. Either a data URI or URL.
  178. * - "x", "y", required decimals. The (x,y) position of the image.
  179. * - "w", "h", required decimals. The width and height of the image.
  180. * - "flipH" and "flipV", optional 0 or 1. Whether to flip the image along the
  181. * horizontal/vertical axis. Default is 0 for both.
  182. *
  183. * If <allowEval> is true, then the text content of the this element can define
  184. * a function which is invoked with the shape as the only argument and returns
  185. * the value for the image source (ignored if the src attribute is not null).
  186. *
  187. * Sub-shapes:
  188. *
  189. * *include-shape* allow stencils to be rendered within the current stencil by
  190. * referencing the sub-stencil by name. Attributes are:
  191. *
  192. * - "name", required string. The unique shape name of the stencil.
  193. * - "x", "y", "w", "h", required decimals. The (x,y) position of the sub-shape
  194. * and its width and height.
  195. *
  196. * Constructor: mxStencil
  197. *
  198. * Constructs a new generic shape by setting <desc> to the given XML node and
  199. * invoking <parseDescription> and <parseConstraints>.
  200. *
  201. * Parameters:
  202. *
  203. * desc - XML node that contains the stencil description.
  204. */
  205. function mxStencil(desc)
  206. {
  207. this.desc = desc;
  208. this.parseDescription();
  209. this.parseConstraints();
  210. };
  211. /**
  212. * Extends mxShape.
  213. */
  214. mxUtils.extend(mxStencil, mxShape);
  215. /**
  216. * Variable: defaultLocalized
  217. *
  218. * Static global variable that specifies the default value for the localized
  219. * attribute of the text element. Default is false.
  220. */
  221. mxStencil.defaultLocalized = false;
  222. /**
  223. * Function: allowEval
  224. *
  225. * Static global switch that specifies if the use of eval is allowed for
  226. * evaluating text content and images. Default is false. Set this to true
  227. * if stencils can not contain user input.
  228. */
  229. mxStencil.allowEval = false;
  230. /**
  231. * Variable: desc
  232. *
  233. * Holds the XML node with the stencil description.
  234. */
  235. mxStencil.prototype.desc = null;
  236. /**
  237. * Variable: constraints
  238. *
  239. * Holds an array of <mxConnectionConstraints> as defined in the shape.
  240. */
  241. mxStencil.prototype.constraints = null;
  242. /**
  243. * Variable: aspect
  244. *
  245. * Holds the aspect of the shape. Default is 'auto'.
  246. */
  247. mxStencil.prototype.aspect = null;
  248. /**
  249. * Variable: w0
  250. *
  251. * Holds the width of the shape. Default is 100.
  252. */
  253. mxStencil.prototype.w0 = null;
  254. /**
  255. * Variable: h0
  256. *
  257. * Holds the height of the shape. Default is 100.
  258. */
  259. mxStencil.prototype.h0 = null;
  260. /**
  261. * Variable: bgNodes
  262. *
  263. * Holds the XML node with the stencil description.
  264. */
  265. mxStencil.prototype.bgNode = null;
  266. /**
  267. * Variable: fgNodes
  268. *
  269. * Holds the XML node with the stencil description.
  270. */
  271. mxStencil.prototype.fgNode = null;
  272. /**
  273. * Variable: strokewidth
  274. *
  275. * Holds the strokewidth direction from the description.
  276. */
  277. mxStencil.prototype.strokewidth = null;
  278. /**
  279. * Function: parseDescription
  280. *
  281. * Reads <w0>, <h0>, <aspect>, <bgNodes> and <fgNodes> from <desc>.
  282. */
  283. mxStencil.prototype.parseDescription = function()
  284. {
  285. // LATER: Preprocess nodes for faster painting
  286. this.fgNode = this.desc.getElementsByTagName('foreground')[0];
  287. this.bgNode = this.desc.getElementsByTagName('background')[0];
  288. this.w0 = Number(this.desc.getAttribute('w') || 100);
  289. this.h0 = Number(this.desc.getAttribute('h') || 100);
  290. // Possible values for aspect are: variable and fixed where
  291. // variable means fill the available space and fixed means
  292. // use w0 and h0 to compute the aspect.
  293. var aspect = this.desc.getAttribute('aspect');
  294. this.aspect = (aspect != null) ? aspect : 'variable';
  295. // Possible values for strokewidth are all numbers and "inherit"
  296. // where the inherit means take the value from the style (ie. the
  297. // user-defined stroke-width). Note that the strokewidth is scaled
  298. // by the minimum scaling that is used to draw the shape (sx, sy).
  299. var sw = this.desc.getAttribute('strokewidth');
  300. this.strokewidth = (sw != null) ? sw : '1';
  301. };
  302. /**
  303. * Function: parseConstraints
  304. *
  305. * Reads the constraints from <desc> into <constraints> using
  306. * <parseConstraint>.
  307. */
  308. mxStencil.prototype.parseConstraints = function()
  309. {
  310. var conns = this.desc.getElementsByTagName('connections')[0];
  311. if (conns != null)
  312. {
  313. var tmp = mxUtils.getChildNodes(conns);
  314. if (tmp != null && tmp.length > 0)
  315. {
  316. this.constraints = [];
  317. for (var i = 0; i < tmp.length; i++)
  318. {
  319. this.constraints.push(this.parseConstraint(tmp[i]));
  320. }
  321. }
  322. }
  323. };
  324. /**
  325. * Function: parseConstraint
  326. *
  327. * Parses the given XML node and returns its <mxConnectionConstraint>.
  328. */
  329. mxStencil.prototype.parseConstraint = function(node)
  330. {
  331. var x = Number(node.getAttribute('x'));
  332. var y = Number(node.getAttribute('y'));
  333. var perimeter = node.getAttribute('perimeter') == '1';
  334. var name = node.getAttribute('name');
  335. return new mxConnectionConstraint(new mxPoint(x, y), perimeter, name);
  336. };
  337. /**
  338. * Function: evaluateTextAttribute
  339. *
  340. * Gets the given attribute as a text. The return value from <evaluateAttribute>
  341. * is used as a key to <mxResources.get> if the localized attribute in the text
  342. * node is 1 or if <defaultLocalized> is true.
  343. */
  344. mxStencil.prototype.evaluateTextAttribute = function(node, attribute, shape)
  345. {
  346. var result = this.evaluateAttribute(node, attribute, shape);
  347. var loc = node.getAttribute('localized');
  348. if ((mxStencil.defaultLocalized && loc == null) || loc == '1')
  349. {
  350. result = mxResources.get(result);
  351. }
  352. return result;
  353. };
  354. /**
  355. * Function: evaluateAttribute
  356. *
  357. * Gets the attribute for the given name from the given node. If the attribute
  358. * does not exist then the text content of the node is evaluated and if it is
  359. * a function it is invoked with <shape> as the only argument and the return
  360. * value is used as the attribute value to be returned.
  361. */
  362. mxStencil.prototype.evaluateAttribute = function(node, attribute, shape)
  363. {
  364. var result = node.getAttribute(attribute);
  365. if (result == null)
  366. {
  367. var text = mxUtils.getTextContent(node);
  368. if (text != null && mxStencil.allowEval)
  369. {
  370. var funct = mxUtils.eval(text);
  371. if (typeof(funct) == 'function')
  372. {
  373. result = funct(shape);
  374. }
  375. }
  376. }
  377. return result;
  378. };
  379. /**
  380. * Function: drawShape
  381. *
  382. * Draws this stencil inside the given bounds.
  383. */
  384. mxStencil.prototype.drawShape = function(canvas, shape, x, y, w, h)
  385. {
  386. var stack = canvas.states.slice();
  387. // TODO: Internal structure (array of special structs?), relative and absolute
  388. // coordinates (eg. note shape, process vs star, actor etc.), text rendering
  389. // and non-proportional scaling, how to implement pluggable edge shapes
  390. // (start, segment, end blocks), pluggable markers, how to implement
  391. // swimlanes (title area) with this API, add icon, horizontal/vertical
  392. // label, indicator for all shapes, rotation
  393. var direction = mxUtils.getValue(shape.style, mxConstants.STYLE_DIRECTION, null);
  394. var aspect = this.computeAspect(shape.style, x, y, w, h, direction);
  395. var minScale = Math.min(aspect.width, aspect.height);
  396. var sw = (this.strokewidth == 'inherit') ?
  397. Number(mxUtils.getNumber(shape.style, mxConstants.STYLE_STROKEWIDTH, 1)) :
  398. Number(this.strokewidth) * minScale;
  399. canvas.setStrokeWidth(sw);
  400. // Draws a transparent rectangle for catching events
  401. if (shape.style != null && mxUtils.getValue(shape.style, mxConstants.STYLE_POINTER_EVENTS, '0') == '1')
  402. {
  403. canvas.setStrokeColor(mxConstants.NONE);
  404. canvas.rect(x, y, w, h);
  405. canvas.stroke();
  406. canvas.setStrokeColor(shape.stroke);
  407. }
  408. this.drawChildren(canvas, shape, x, y, w, h, this.bgNode, aspect, false, true);
  409. this.drawChildren(canvas, shape, x, y, w, h, this.fgNode, aspect, true,
  410. !shape.outline || shape.style == null || mxUtils.getValue(
  411. shape.style, mxConstants.STYLE_BACKGROUND_OUTLINE, 0) == 0);
  412. // Restores stack for unequal count of save/restore calls
  413. if (canvas.states.length != stack.length)
  414. {
  415. canvas.states = stack;
  416. }
  417. };
  418. /**
  419. * Function: drawChildren
  420. *
  421. * Draws this stencil inside the given bounds.
  422. */
  423. mxStencil.prototype.drawChildren = function(canvas, shape, x, y, w, h, node, aspect, disableShadow, paint)
  424. {
  425. if (node != null && w > 0 && h > 0)
  426. {
  427. var tmp = node.firstChild;
  428. while (tmp != null)
  429. {
  430. if (tmp.nodeType == mxConstants.NODETYPE_ELEMENT)
  431. {
  432. this.drawNode(canvas, shape, tmp, aspect, disableShadow, paint);
  433. }
  434. tmp = tmp.nextSibling;
  435. }
  436. }
  437. };
  438. /**
  439. * Function: computeAspect
  440. *
  441. * Returns a rectangle that contains the offset in x and y and the horizontal
  442. * and vertical scale in width and height used to draw this shape inside the
  443. * given <mxRectangle>.
  444. *
  445. * Parameters:
  446. *
  447. * shape - <mxShape> to be drawn.
  448. * bounds - <mxRectangle> that should contain the stencil.
  449. * direction - Optional direction of the shape to be darwn.
  450. */
  451. mxStencil.prototype.computeAspect = function(shape, x, y, w, h, direction)
  452. {
  453. var x0 = x;
  454. var y0 = y;
  455. var sx = w / this.w0;
  456. var sy = h / this.h0;
  457. var inverse = (direction == mxConstants.DIRECTION_NORTH || direction == mxConstants.DIRECTION_SOUTH);
  458. if (inverse)
  459. {
  460. sy = w / this.h0;
  461. sx = h / this.w0;
  462. var delta = (w - h) / 2;
  463. x0 += delta;
  464. y0 -= delta;
  465. }
  466. if (this.aspect == 'fixed')
  467. {
  468. sy = Math.min(sx, sy);
  469. sx = sy;
  470. // Centers the shape inside the available space
  471. if (inverse)
  472. {
  473. x0 += (h - this.w0 * sx) / 2;
  474. y0 += (w - this.h0 * sy) / 2;
  475. }
  476. else
  477. {
  478. x0 += (w - this.w0 * sx) / 2;
  479. y0 += (h - this.h0 * sy) / 2;
  480. }
  481. }
  482. return new mxRectangle(x0, y0, sx, sy);
  483. };
  484. /**
  485. * Function: parseColor
  486. *
  487. * Returns the color for given value.
  488. */
  489. mxStencil.prototype.parseColor = function(canvas, shape, node, value)
  490. {
  491. if (value == 'stroke')
  492. {
  493. value = shape.stroke;
  494. }
  495. else if (value == 'fill')
  496. {
  497. value = shape.fill;
  498. }
  499. return value;
  500. };
  501. /**
  502. * Function: drawNode
  503. *
  504. * Draws this stencil inside the given bounds.
  505. */
  506. mxStencil.prototype.drawNode = function(canvas, shape, node, aspect, disableShadow, paint)
  507. {
  508. var name = node.nodeName;
  509. var x0 = aspect.x;
  510. var y0 = aspect.y;
  511. var sx = aspect.width;
  512. var sy = aspect.height;
  513. var minScale = Math.min(sx, sy);
  514. if (name == 'save')
  515. {
  516. canvas.save();
  517. }
  518. else if (name == 'restore')
  519. {
  520. canvas.restore();
  521. }
  522. else if (paint)
  523. {
  524. if (name == 'path')
  525. {
  526. canvas.begin();
  527. var parseRegularly = true;
  528. if (node.getAttribute('rounded') == '1')
  529. {
  530. parseRegularly = false;
  531. var arcSize = Number(node.getAttribute('arcSize'));
  532. var pointCount = 0;
  533. var segs = [];
  534. // Renders the elements inside the given path
  535. var childNode = node.firstChild;
  536. while (childNode != null)
  537. {
  538. if (childNode.nodeType == mxConstants.NODETYPE_ELEMENT)
  539. {
  540. var childName = childNode.nodeName;
  541. if (childName == 'move' || childName == 'line')
  542. {
  543. if (childName == 'move' || segs.length == 0)
  544. {
  545. segs.push([]);
  546. }
  547. segs[segs.length - 1].push(new mxPoint(x0 + Number(childNode.getAttribute('x')) * sx,
  548. y0 + Number(childNode.getAttribute('y')) * sy));
  549. pointCount++;
  550. }
  551. else
  552. {
  553. //We only support move and line for rounded corners
  554. parseRegularly = true;
  555. break;
  556. }
  557. }
  558. childNode = childNode.nextSibling;
  559. }
  560. if (!parseRegularly && pointCount > 0)
  561. {
  562. for (var i = 0; i < segs.length; i++)
  563. {
  564. var close = false, ps = segs[i][0], pe = segs[i][segs[i].length - 1];
  565. if (ps.x == pe.x && ps.y == pe.y)
  566. {
  567. segs[i].pop();
  568. close = true;
  569. }
  570. this.addPoints(canvas, segs[i], true, arcSize, close);
  571. }
  572. }
  573. else
  574. {
  575. parseRegularly = true;
  576. }
  577. }
  578. if (parseRegularly)
  579. {
  580. // Renders the elements inside the given path
  581. var childNode = node.firstChild;
  582. while (childNode != null)
  583. {
  584. if (childNode.nodeType == mxConstants.NODETYPE_ELEMENT)
  585. {
  586. this.drawNode(canvas, shape, childNode, aspect, disableShadow, paint);
  587. }
  588. childNode = childNode.nextSibling;
  589. }
  590. }
  591. }
  592. else if (name == 'close')
  593. {
  594. canvas.close();
  595. }
  596. else if (name == 'move')
  597. {
  598. canvas.moveTo(x0 + Number(node.getAttribute('x')) * sx, y0 + Number(node.getAttribute('y')) * sy);
  599. }
  600. else if (name == 'line')
  601. {
  602. canvas.lineTo(x0 + Number(node.getAttribute('x')) * sx, y0 + Number(node.getAttribute('y')) * sy);
  603. }
  604. else if (name == 'quad')
  605. {
  606. canvas.quadTo(x0 + Number(node.getAttribute('x1')) * sx,
  607. y0 + Number(node.getAttribute('y1')) * sy,
  608. x0 + Number(node.getAttribute('x2')) * sx,
  609. y0 + Number(node.getAttribute('y2')) * sy);
  610. }
  611. else if (name == 'curve')
  612. {
  613. canvas.curveTo(x0 + Number(node.getAttribute('x1')) * sx,
  614. y0 + Number(node.getAttribute('y1')) * sy,
  615. x0 + Number(node.getAttribute('x2')) * sx,
  616. y0 + Number(node.getAttribute('y2')) * sy,
  617. x0 + Number(node.getAttribute('x3')) * sx,
  618. y0 + Number(node.getAttribute('y3')) * sy);
  619. }
  620. else if (name == 'arc')
  621. {
  622. canvas.arcTo(Number(node.getAttribute('rx')) * sx,
  623. Number(node.getAttribute('ry')) * sy,
  624. Number(node.getAttribute('x-axis-rotation')),
  625. Number(node.getAttribute('large-arc-flag')),
  626. Number(node.getAttribute('sweep-flag')),
  627. x0 + Number(node.getAttribute('x')) * sx,
  628. y0 + Number(node.getAttribute('y')) * sy);
  629. }
  630. else if (name == 'rect')
  631. {
  632. canvas.rect(x0 + Number(node.getAttribute('x')) * sx,
  633. y0 + Number(node.getAttribute('y')) * sy,
  634. Number(node.getAttribute('w')) * sx,
  635. Number(node.getAttribute('h')) * sy);
  636. }
  637. else if (name == 'roundrect')
  638. {
  639. var arcsize = Number(node.getAttribute('arcsize'));
  640. if (arcsize == 0)
  641. {
  642. arcsize = mxConstants.RECTANGLE_ROUNDING_FACTOR * 100;
  643. }
  644. var w = Number(node.getAttribute('w')) * sx;
  645. var h = Number(node.getAttribute('h')) * sy;
  646. var factor = Number(arcsize) / 100;
  647. var r = Math.min(w * factor, h * factor);
  648. canvas.roundrect(x0 + Number(node.getAttribute('x')) * sx,
  649. y0 + Number(node.getAttribute('y')) * sy,
  650. w, h, r, r);
  651. }
  652. else if (name == 'ellipse')
  653. {
  654. canvas.ellipse(x0 + Number(node.getAttribute('x')) * sx,
  655. y0 + Number(node.getAttribute('y')) * sy,
  656. Number(node.getAttribute('w')) * sx,
  657. Number(node.getAttribute('h')) * sy);
  658. }
  659. else if (name == 'image')
  660. {
  661. if (!shape.outline)
  662. {
  663. var src = this.evaluateAttribute(node, 'src', shape);
  664. canvas.image(x0 + Number(node.getAttribute('x')) * sx,
  665. y0 + Number(node.getAttribute('y')) * sy,
  666. Number(node.getAttribute('w')) * sx,
  667. Number(node.getAttribute('h')) * sy,
  668. src, false, node.getAttribute('flipH') == '1',
  669. node.getAttribute('flipV') == '1');
  670. }
  671. }
  672. else if (name == 'text')
  673. {
  674. if (!shape.outline)
  675. {
  676. var str = this.evaluateTextAttribute(node, 'str', shape);
  677. var rotation = node.getAttribute('vertical') == '1' ? -90 : 0;
  678. if (node.getAttribute('align-shape') == '0')
  679. {
  680. var dr = shape.rotation;
  681. // Depends on flipping
  682. var flipH = mxUtils.getValue(shape.style, mxConstants.STYLE_FLIPH, 0) == 1;
  683. var flipV = mxUtils.getValue(shape.style, mxConstants.STYLE_FLIPV, 0) == 1;
  684. if (flipH && flipV)
  685. {
  686. rotation -= dr;
  687. }
  688. else if (flipH || flipV)
  689. {
  690. rotation += dr;
  691. }
  692. else
  693. {
  694. rotation -= dr;
  695. }
  696. }
  697. rotation -= node.getAttribute('rotation');
  698. canvas.text(x0 + Number(node.getAttribute('x')) * sx,
  699. y0 + Number(node.getAttribute('y')) * sy,
  700. 0, 0, str, node.getAttribute('align') || 'left',
  701. node.getAttribute('valign') || 'top', false, '',
  702. null, false, rotation);
  703. }
  704. }
  705. else if (name == 'include-shape')
  706. {
  707. var stencil = mxStencilRegistry.getStencil(node.getAttribute('name'));
  708. if (stencil != null)
  709. {
  710. var x = x0 + Number(node.getAttribute('x')) * sx;
  711. var y = y0 + Number(node.getAttribute('y')) * sy;
  712. var w = Number(node.getAttribute('w')) * sx;
  713. var h = Number(node.getAttribute('h')) * sy;
  714. stencil.drawShape(canvas, shape, x, y, w, h);
  715. }
  716. }
  717. else if (name == 'fillstroke')
  718. {
  719. canvas.fillAndStroke();
  720. }
  721. else if (name == 'fill')
  722. {
  723. canvas.fill();
  724. }
  725. else if (name == 'stroke')
  726. {
  727. canvas.stroke();
  728. }
  729. else if (name == 'strokewidth')
  730. {
  731. var s = (node.getAttribute('fixed') == '1') ? 1 : minScale;
  732. canvas.setStrokeWidth(Number(node.getAttribute('width')) * s);
  733. }
  734. else if (name == 'dashed')
  735. {
  736. canvas.setDashed(node.getAttribute('dashed') == '1');
  737. }
  738. else if (name == 'dashpattern')
  739. {
  740. var value = node.getAttribute('pattern');
  741. if (value != null)
  742. {
  743. var tmp = value.split(' ');
  744. var pat = [];
  745. for (var i = 0; i < tmp.length; i++)
  746. {
  747. if (tmp[i].length > 0)
  748. {
  749. pat.push(Number(tmp[i]) * minScale);
  750. }
  751. }
  752. value = pat.join(' ');
  753. canvas.setDashPattern(value);
  754. }
  755. }
  756. else if (name == 'strokecolor')
  757. {
  758. canvas.setStrokeColor(this.parseColor(canvas, shape, node, node.getAttribute('color')));
  759. }
  760. else if (name == 'linecap')
  761. {
  762. canvas.setLineCap(node.getAttribute('cap'));
  763. }
  764. else if (name == 'linejoin')
  765. {
  766. canvas.setLineJoin(node.getAttribute('join'));
  767. }
  768. else if (name == 'miterlimit')
  769. {
  770. canvas.setMiterLimit(Number(node.getAttribute('limit')));
  771. }
  772. else if (name == 'fillcolor')
  773. {
  774. canvas.setFillColor(this.parseColor(canvas, shape, node, node.getAttribute('color')));
  775. }
  776. else if (name == 'alpha')
  777. {
  778. canvas.setAlpha(node.getAttribute('alpha'));
  779. }
  780. else if (name == 'fillalpha')
  781. {
  782. canvas.setAlpha(node.getAttribute('alpha'));
  783. }
  784. else if (name == 'strokealpha')
  785. {
  786. canvas.setAlpha(node.getAttribute('alpha'));
  787. }
  788. else if (name == 'fontcolor')
  789. {
  790. canvas.setFontColor(this.parseColor(canvas, shape, node, node.getAttribute('color')));
  791. }
  792. else if (name == 'fontstyle')
  793. {
  794. canvas.setFontStyle(node.getAttribute('style'));
  795. }
  796. else if (name == 'fontfamily')
  797. {
  798. canvas.setFontFamily(node.getAttribute('family'));
  799. }
  800. else if (name == 'fontsize')
  801. {
  802. canvas.setFontSize(Number(node.getAttribute('size')) * minScale);
  803. }
  804. if (disableShadow && (name == 'fillstroke' || name == 'fill' || name == 'stroke'))
  805. {
  806. disableShadow = false;
  807. canvas.setShadow(false);
  808. }
  809. }
  810. };