mxShape.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674
  1. /**
  2. * Copyright (c) 2006-2015, JGraph Ltd
  3. * Copyright (c) 2006-2015, Gaudenz Alder
  4. */
  5. /**
  6. * Class: mxShape
  7. *
  8. * Base class for all shapes. A shape in mxGraph is a
  9. * separate implementation for SVG, VML and HTML. Which
  10. * implementation to use is controlled by the <dialect>
  11. * property which is assigned from within the <mxCellRenderer>
  12. * when the shape is created. The dialect must be assigned
  13. * for a shape, and it does normally depend on the browser and
  14. * the confiuration of the graph (see <mxGraph> rendering hint).
  15. *
  16. * For each supported shape in SVG and VML, a corresponding
  17. * shape exists in mxGraph, namely for text, image, rectangle,
  18. * rhombus, ellipse and polyline. The other shapes are a
  19. * combination of these shapes (eg. label and swimlane)
  20. * or they consist of one or more (filled) path objects
  21. * (eg. actor and cylinder). The HTML implementation is
  22. * optional but may be required for a HTML-only view of
  23. * the graph.
  24. *
  25. * Custom Shapes:
  26. *
  27. * To extend from this class, the basic code looks as follows.
  28. * In the special case where the custom shape consists only of
  29. * one filled region or one filled region and an additional stroke
  30. * the <mxActor> and <mxCylinder> should be subclassed,
  31. * respectively.
  32. *
  33. * (code)
  34. * function CustomShape() { }
  35. *
  36. * CustomShape.prototype = new mxShape();
  37. * CustomShape.prototype.constructor = CustomShape;
  38. * (end)
  39. *
  40. * To register a custom shape in an existing graph instance,
  41. * one must register the shape under a new name in the graph's
  42. * cell renderer as follows:
  43. *
  44. * (code)
  45. * mxCellRenderer.registerShape('customShape', CustomShape);
  46. * (end)
  47. *
  48. * The second argument is the name of the constructor.
  49. *
  50. * In order to use the shape you can refer to the given name above
  51. * in a stylesheet. For example, to change the shape for the default
  52. * vertex style, the following code is used:
  53. *
  54. * (code)
  55. * var style = graph.getStylesheet().getDefaultVertexStyle();
  56. * style[mxConstants.STYLE_SHAPE] = 'customShape';
  57. * (end)
  58. *
  59. * Constructor: mxShape
  60. *
  61. * Constructs a new shape.
  62. */
  63. function mxShape(stencil)
  64. {
  65. this.stencil = stencil;
  66. this.initStyles();
  67. };
  68. /**
  69. * Variable: dialect
  70. *
  71. * Holds the dialect in which the shape is to be painted.
  72. * This can be one of the DIALECT constants in <mxConstants>.
  73. */
  74. mxShape.prototype.dialect = null;
  75. /**
  76. * Variable: scale
  77. *
  78. * Holds the scale in which the shape is being painted.
  79. */
  80. mxShape.prototype.scale = 1;
  81. /**
  82. * Variable: antiAlias
  83. *
  84. * Rendering hint for configuring the canvas.
  85. */
  86. mxShape.prototype.antiAlias = true;
  87. /**
  88. * Variable: minSvgStrokeWidth
  89. *
  90. * Minimum stroke width for SVG output.
  91. */
  92. mxShape.prototype.minSvgStrokeWidth = 1;
  93. /**
  94. * Variable: bounds
  95. *
  96. * Holds the <mxRectangle> that specifies the bounds of this shape.
  97. */
  98. mxShape.prototype.bounds = null;
  99. /**
  100. * Variable: points
  101. *
  102. * Holds the array of <mxPoints> that specify the points of this shape.
  103. */
  104. mxShape.prototype.points = null;
  105. /**
  106. * Variable: node
  107. *
  108. * Holds the outermost DOM node that represents this shape.
  109. */
  110. mxShape.prototype.node = null;
  111. /**
  112. * Variable: state
  113. *
  114. * Optional reference to the corresponding <mxCellState>.
  115. */
  116. mxShape.prototype.state = null;
  117. /**
  118. * Variable: style
  119. *
  120. * Optional reference to the style of the corresponding <mxCellState>.
  121. */
  122. mxShape.prototype.style = null;
  123. /**
  124. * Variable: boundingBox
  125. *
  126. * Contains the bounding box of the shape, that is, the smallest rectangle
  127. * that includes all pixels of the shape.
  128. */
  129. mxShape.prototype.boundingBox = null;
  130. /**
  131. * Variable: stencil
  132. *
  133. * Holds the <mxStencil> that defines the shape.
  134. */
  135. mxShape.prototype.stencil = null;
  136. /**
  137. * Variable: svgStrokeTolerance
  138. *
  139. * Event-tolerance for SVG strokes (in px). Default is 8. This is only passed
  140. * to the canvas in <createSvgCanvas> if <pointerEvents> is true.
  141. */
  142. mxShape.prototype.svgStrokeTolerance = 8;
  143. /**
  144. * Variable: pointerEvents
  145. *
  146. * Specifies if pointer events should be handled. Default is true.
  147. */
  148. mxShape.prototype.pointerEvents = true;
  149. /**
  150. * Variable: svgPointerEvents
  151. *
  152. * Specifies if pointer events should be handled. Default is true.
  153. */
  154. mxShape.prototype.svgPointerEvents = 'all';
  155. /**
  156. * Variable: shapePointerEvents
  157. *
  158. * Specifies if pointer events outside of shape should be handled. Default
  159. * is false.
  160. */
  161. mxShape.prototype.shapePointerEvents = false;
  162. /**
  163. * Variable: stencilPointerEvents
  164. *
  165. * Specifies if pointer events outside of stencils should be handled. Default
  166. * is false. Set this to true for backwards compatibility with the 1.x branch.
  167. */
  168. mxShape.prototype.stencilPointerEvents = false;
  169. /**
  170. * Variable: vmlScale
  171. *
  172. * Scale for improving the precision of VML rendering. Default is 1.
  173. */
  174. mxShape.prototype.vmlScale = 1;
  175. /**
  176. * Variable: outline
  177. *
  178. * Specifies if the shape should be drawn as an outline. This disables all
  179. * fill colors and can be used to disable other drawing states that should
  180. * not be painted for outlines. Default is false. This should be set before
  181. * calling <apply>.
  182. */
  183. mxShape.prototype.outline = false;
  184. /**
  185. * Variable: visible
  186. *
  187. * Specifies if the shape is visible. Default is true.
  188. */
  189. mxShape.prototype.visible = true;
  190. /**
  191. * Variable: useSvgBoundingBox
  192. *
  193. * Allows to use the SVG bounding box in SVG. Default is false for performance
  194. * reasons.
  195. */
  196. mxShape.prototype.useSvgBoundingBox = false;
  197. /**
  198. * Function: init
  199. *
  200. * Initializes the shape by creaing the DOM node using <create>
  201. * and adding it into the given container.
  202. *
  203. * Parameters:
  204. *
  205. * container - DOM node that will contain the shape.
  206. */
  207. mxShape.prototype.init = function(container)
  208. {
  209. if (this.node == null)
  210. {
  211. this.node = this.create(container);
  212. if (container != null)
  213. {
  214. container.appendChild(this.node);
  215. }
  216. }
  217. };
  218. /**
  219. * Function: initStyles
  220. *
  221. * Sets the styles to their default values.
  222. */
  223. mxShape.prototype.initStyles = function(container)
  224. {
  225. this.strokewidth = 1;
  226. this.rotation = 0;
  227. this.opacity = 100;
  228. this.fillOpacity = 100;
  229. this.strokeOpacity = 100;
  230. this.flipH = false;
  231. this.flipV = false;
  232. };
  233. /**
  234. * Function: isParseVml
  235. *
  236. * Specifies if any VML should be added via insertAdjacentHtml to the DOM. This
  237. * is only needed in IE8 and only if the shape contains VML markup. This method
  238. * returns true.
  239. */
  240. mxShape.prototype.isParseVml = function()
  241. {
  242. return true;
  243. };
  244. /**
  245. * Function: isHtmlAllowed
  246. *
  247. * Returns true if HTML is allowed for this shape. This implementation always
  248. * returns false.
  249. */
  250. mxShape.prototype.isHtmlAllowed = function()
  251. {
  252. return false;
  253. };
  254. /**
  255. * Function: getSvgScreenOffset
  256. *
  257. * Returns 0, or 0.5 if <strokewidth> % 2 == 1.
  258. */
  259. mxShape.prototype.getSvgScreenOffset = function()
  260. {
  261. var sw = this.stencil && this.stencil.strokewidth != 'inherit' ? Number(this.stencil.strokewidth) : this.strokewidth;
  262. return (mxUtils.mod(Math.max(1, Math.round(sw * this.scale)), 2) == 1) ? 0.5 : 0;
  263. };
  264. /**
  265. * Function: create
  266. *
  267. * Creates and returns the DOM node(s) for the shape in
  268. * the given container. This implementation invokes
  269. * <createSvg>, <createHtml> or <createVml> depending
  270. * on the <dialect> and style settings.
  271. *
  272. * Parameters:
  273. *
  274. * container - DOM node that will contain the shape.
  275. */
  276. mxShape.prototype.create = function(container)
  277. {
  278. var node = null;
  279. if (container != null && container.ownerSVGElement != null)
  280. {
  281. node = this.createSvg(container);
  282. }
  283. else if (document.documentMode == 8 || !mxClient.IS_VML ||
  284. (this.dialect != mxConstants.DIALECT_VML && this.isHtmlAllowed()))
  285. {
  286. node = this.createHtml(container);
  287. }
  288. else
  289. {
  290. node = this.createVml(container);
  291. }
  292. return node;
  293. };
  294. /**
  295. * Function: createSvg
  296. *
  297. * Creates and returns the SVG node(s) to represent this shape.
  298. */
  299. mxShape.prototype.createSvg = function()
  300. {
  301. return document.createElementNS(mxConstants.NS_SVG, 'g');
  302. };
  303. /**
  304. * Function: createVml
  305. *
  306. * Creates and returns the VML node to represent this shape.
  307. */
  308. mxShape.prototype.createVml = function()
  309. {
  310. var node = document.createElement(mxClient.VML_PREFIX + ':group');
  311. node.style.position = 'absolute';
  312. return node;
  313. };
  314. /**
  315. * Function: createHtml
  316. *
  317. * Creates and returns the HTML DOM node(s) to represent
  318. * this shape. This implementation falls back to <createVml>
  319. * so that the HTML creation is optional.
  320. */
  321. mxShape.prototype.createHtml = function()
  322. {
  323. var node = document.createElement('div');
  324. node.style.position = 'absolute';
  325. return node;
  326. };
  327. /**
  328. * Function: reconfigure
  329. *
  330. * Reconfigures this shape. This will update the colors etc in
  331. * addition to the bounds or points.
  332. */
  333. mxShape.prototype.reconfigure = function()
  334. {
  335. this.redraw();
  336. };
  337. /**
  338. * Function: redraw
  339. *
  340. * Creates and returns the SVG node(s) to represent this shape.
  341. */
  342. mxShape.prototype.redraw = function()
  343. {
  344. this.updateBoundsFromPoints();
  345. if (this.visible && this.checkBounds())
  346. {
  347. this.node.style.visibility = 'visible';
  348. this.clear();
  349. if (this.node.nodeName == 'DIV' && (this.isHtmlAllowed() || !mxClient.IS_VML))
  350. {
  351. this.redrawHtmlShape();
  352. }
  353. else
  354. {
  355. this.redrawShape();
  356. }
  357. this.updateBoundingBox();
  358. }
  359. else
  360. {
  361. this.node.style.visibility = 'hidden';
  362. this.boundingBox = null;
  363. }
  364. };
  365. /**
  366. * Function: clear
  367. *
  368. * Removes all child nodes and resets all CSS.
  369. */
  370. mxShape.prototype.clear = function()
  371. {
  372. if (this.node.ownerSVGElement != null)
  373. {
  374. while (this.node.lastChild != null)
  375. {
  376. this.node.removeChild(this.node.lastChild);
  377. }
  378. }
  379. else
  380. {
  381. this.node.style.cssText = 'position:absolute;' + ((this.cursor != null) ?
  382. ('cursor:' + this.cursor + ';') : '');
  383. this.node.innerHTML = '';
  384. }
  385. };
  386. /**
  387. * Function: updateBoundsFromPoints
  388. *
  389. * Updates the bounds based on the points.
  390. */
  391. mxShape.prototype.updateBoundsFromPoints = function()
  392. {
  393. var pts = this.points;
  394. if (pts != null && pts.length > 0 && pts[0] != null)
  395. {
  396. this.bounds = new mxRectangle(Number(pts[0].x), Number(pts[0].y), 1, 1);
  397. for (var i = 1; i < this.points.length; i++)
  398. {
  399. if (pts[i] != null)
  400. {
  401. this.bounds.add(new mxRectangle(Number(pts[i].x), Number(pts[i].y), 1, 1));
  402. }
  403. }
  404. }
  405. };
  406. /**
  407. * Function: getLabelBounds
  408. *
  409. * Returns the <mxRectangle> for the label bounds of this shape, based on the
  410. * given scaled and translated bounds of the shape. This method should not
  411. * change the rectangle in-place. This implementation returns the given rect.
  412. */
  413. mxShape.prototype.getLabelBounds = function(rect)
  414. {
  415. var d = mxUtils.getValue(this.style, mxConstants.STYLE_DIRECTION, mxConstants.DIRECTION_EAST);
  416. var bounds = rect;
  417. // Normalizes argument for getLabelMargins hook
  418. if (d != mxConstants.DIRECTION_SOUTH && d != mxConstants.DIRECTION_NORTH &&
  419. this.state != null && this.state.text != null &&
  420. this.state.text.isPaintBoundsInverted())
  421. {
  422. bounds = bounds.clone();
  423. var tmp = bounds.width;
  424. bounds.width = bounds.height;
  425. bounds.height = tmp;
  426. }
  427. var m = this.getLabelMargins(bounds);
  428. if (m != null)
  429. {
  430. var flipH = mxUtils.getValue(this.style, mxConstants.STYLE_FLIPH, false) == '1';
  431. var flipV = mxUtils.getValue(this.style, mxConstants.STYLE_FLIPV, false) == '1';
  432. // Handles special case for vertical labels
  433. if (this.state != null && this.state.text != null &&
  434. this.state.text.isPaintBoundsInverted())
  435. {
  436. var tmp = m.x;
  437. m.x = m.height;
  438. m.height = m.width;
  439. m.width = m.y;
  440. m.y = tmp;
  441. tmp = flipH;
  442. flipH = flipV;
  443. flipV = tmp;
  444. }
  445. return mxUtils.getDirectedBounds(rect, m, this.style, flipH, flipV);
  446. }
  447. return rect;
  448. };
  449. /**
  450. * Function: getLabelMargins
  451. *
  452. * Returns the scaled top, left, bottom and right margin to be used for
  453. * computing the label bounds as an <mxRectangle>, where the bottom and right
  454. * margin are defined in the width and height of the rectangle, respectively.
  455. */
  456. mxShape.prototype.getLabelMargins= function(rect)
  457. {
  458. return null;
  459. };
  460. /**
  461. * Function: checkBounds
  462. *
  463. * Returns true if the bounds are not null and all of its variables are numeric.
  464. */
  465. mxShape.prototype.checkBounds = function()
  466. {
  467. return (!isNaN(this.scale) && isFinite(this.scale) && this.scale > 0 &&
  468. this.bounds != null && !isNaN(this.bounds.x) && !isNaN(this.bounds.y) &&
  469. !isNaN(this.bounds.width) && !isNaN(this.bounds.height) &&
  470. this.bounds.width > 0 && this.bounds.height > 0);
  471. };
  472. /**
  473. * Function: createVmlGroup
  474. *
  475. * Returns the temporary element used for rendering in IE8 standards mode.
  476. */
  477. mxShape.prototype.createVmlGroup = function()
  478. {
  479. var node = document.createElement(mxClient.VML_PREFIX + ':group');
  480. node.style.position = 'absolute';
  481. node.style.width = this.node.style.width;
  482. node.style.height = this.node.style.height;
  483. return node;
  484. };
  485. /**
  486. * Function: redrawShape
  487. *
  488. * Updates the SVG or VML shape.
  489. */
  490. mxShape.prototype.redrawShape = function()
  491. {
  492. var canvas = this.createCanvas();
  493. if (canvas != null)
  494. {
  495. // Specifies if events should be handled
  496. canvas.pointerEvents = this.pointerEvents;
  497. this.beforePaint(canvas);
  498. this.paint(canvas);
  499. this.afterPaint(canvas);
  500. if (this.node != canvas.root)
  501. {
  502. // Forces parsing in IE8 standards mode - slow! avoid
  503. this.node.insertAdjacentHTML('beforeend', canvas.root.outerHTML);
  504. }
  505. if (this.node.nodeName == 'DIV' && document.documentMode == 8)
  506. {
  507. // Makes DIV transparent to events for IE8 in IE8 standards
  508. // mode (Note: Does not work for IE9 in IE8 standards mode
  509. // and not for IE11 in enterprise mode)
  510. this.node.style.filter = '';
  511. // Adds event transparency in IE8 standards
  512. mxUtils.addTransparentBackgroundFilter(this.node);
  513. }
  514. this.destroyCanvas(canvas);
  515. }
  516. };
  517. /**
  518. * Function: createCanvas
  519. *
  520. * Creates a new canvas for drawing this shape. May return null.
  521. */
  522. mxShape.prototype.createCanvas = function()
  523. {
  524. var canvas = null;
  525. // LATER: Check if reusing existing DOM nodes improves performance
  526. if (this.node.ownerSVGElement != null)
  527. {
  528. canvas = this.createSvgCanvas();
  529. }
  530. else if (mxClient.IS_VML)
  531. {
  532. this.updateVmlContainer();
  533. canvas = this.createVmlCanvas();
  534. }
  535. if (canvas != null && this.outline)
  536. {
  537. canvas.setStrokeWidth(this.strokewidth);
  538. canvas.setStrokeColor(this.stroke);
  539. if (this.isDashed != null)
  540. {
  541. canvas.setDashed(this.isDashed);
  542. }
  543. canvas.setStrokeWidth = function() {};
  544. canvas.setStrokeColor = function() {};
  545. canvas.setFillColor = function() {};
  546. canvas.setGradient = function() {};
  547. canvas.setDashed = function() {};
  548. canvas.text = function() {};
  549. }
  550. return canvas;
  551. };
  552. /**
  553. * Function: createSvgCanvas
  554. *
  555. * Creates and returns an <mxSvgCanvas2D> for rendering this shape.
  556. */
  557. mxShape.prototype.createSvgCanvas = function()
  558. {
  559. var canvas = new mxSvgCanvas2D(this.node, false);
  560. canvas.strokeTolerance = (this.pointerEvents) ? this.svgStrokeTolerance : 0;
  561. canvas.pointerEventsValue = this.svgPointerEvents;
  562. var off = this.getSvgScreenOffset();
  563. if (off != 0)
  564. {
  565. this.node.setAttribute('transform', 'translate(' + off + ',' + off + ')');
  566. }
  567. else
  568. {
  569. this.node.removeAttribute('transform');
  570. }
  571. canvas.minStrokeWidth = this.minSvgStrokeWidth;
  572. if (!this.antiAlias)
  573. {
  574. // Rounds all numbers in the SVG output to integers
  575. canvas.format = function(value)
  576. {
  577. return Math.round(parseFloat(value));
  578. };
  579. }
  580. return canvas;
  581. };
  582. /**
  583. * Function: createVmlCanvas
  584. *
  585. * Creates and returns an <mxVmlCanvas2D> for rendering this shape.
  586. */
  587. mxShape.prototype.createVmlCanvas = function()
  588. {
  589. // Workaround for VML rendering bug in IE8 standards mode
  590. var node = (document.documentMode == 8 && this.isParseVml()) ? this.createVmlGroup() : this.node;
  591. var canvas = new mxVmlCanvas2D(node, false);
  592. if (node.tagUrn != '')
  593. {
  594. var w = Math.max(1, Math.round(this.bounds.width));
  595. var h = Math.max(1, Math.round(this.bounds.height));
  596. node.coordsize = (w * this.vmlScale) + ',' + (h * this.vmlScale);
  597. canvas.scale(this.vmlScale);
  598. canvas.vmlScale = this.vmlScale;
  599. }
  600. // Painting relative to top, left shape corner
  601. var s = this.scale;
  602. canvas.translate(-Math.round(this.bounds.x / s), -Math.round(this.bounds.y / s));
  603. return canvas;
  604. };
  605. /**
  606. * Function: updateVmlContainer
  607. *
  608. * Updates the bounds of the VML container.
  609. */
  610. mxShape.prototype.updateVmlContainer = function()
  611. {
  612. this.node.style.left = Math.round(this.bounds.x) + 'px';
  613. this.node.style.top = Math.round(this.bounds.y) + 'px';
  614. var w = Math.max(1, Math.round(this.bounds.width));
  615. var h = Math.max(1, Math.round(this.bounds.height));
  616. this.node.style.width = w + 'px';
  617. this.node.style.height = h + 'px';
  618. this.node.style.overflow = 'visible';
  619. };
  620. /**
  621. * Function: redrawHtml
  622. *
  623. * Allow optimization by replacing VML with HTML.
  624. */
  625. mxShape.prototype.redrawHtmlShape = function()
  626. {
  627. // LATER: Refactor methods
  628. this.updateHtmlBounds(this.node);
  629. this.updateHtmlFilters(this.node);
  630. this.updateHtmlColors(this.node);
  631. };
  632. /**
  633. * Function: updateHtmlFilters
  634. *
  635. * Allow optimization by replacing VML with HTML.
  636. */
  637. mxShape.prototype.updateHtmlFilters = function(node)
  638. {
  639. var f = '';
  640. if (this.opacity < 100)
  641. {
  642. f += 'alpha(opacity=' + (this.opacity) + ')';
  643. }
  644. if (this.isShadow)
  645. {
  646. // FIXME: Cannot implement shadow transparency with filter
  647. f += 'progid:DXImageTransform.Microsoft.dropShadow (' +
  648. 'OffX=\'' + Math.round(mxConstants.SHADOW_OFFSET_X * this.scale) + '\', ' +
  649. 'OffY=\'' + Math.round(mxConstants.SHADOW_OFFSET_Y * this.scale) + '\', ' +
  650. 'Color=\'' + mxConstants.VML_SHADOWCOLOR + '\')';
  651. }
  652. if (this.fill != null && this.fill != mxConstants.NONE && this.gradient && this.gradient != mxConstants.NONE)
  653. {
  654. var start = this.fill;
  655. var end = this.gradient;
  656. var type = '0';
  657. var lookup = {east:0,south:1,west:2,north:3};
  658. var dir = (this.direction != null) ? lookup[this.direction] : 0;
  659. if (this.gradientDirection != null)
  660. {
  661. dir = mxUtils.mod(dir + lookup[this.gradientDirection] - 1, 4);
  662. }
  663. if (dir == 1)
  664. {
  665. type = '1';
  666. var tmp = start;
  667. start = end;
  668. end = tmp;
  669. }
  670. else if (dir == 2)
  671. {
  672. var tmp = start;
  673. start = end;
  674. end = tmp;
  675. }
  676. else if (dir == 3)
  677. {
  678. type = '1';
  679. }
  680. f += 'progid:DXImageTransform.Microsoft.gradient(' +
  681. 'startColorStr=\'' + start + '\', endColorStr=\'' + end +
  682. '\', gradientType=\'' + type + '\')';
  683. }
  684. node.style.filter = f;
  685. };
  686. /**
  687. * Function: updateHtmlColors
  688. *
  689. * Allow optimization by replacing VML with HTML.
  690. */
  691. mxShape.prototype.updateHtmlColors = function(node)
  692. {
  693. var color = this.stroke;
  694. if (color != null && color != mxConstants.NONE)
  695. {
  696. node.style.borderColor = color;
  697. if (this.isDashed)
  698. {
  699. node.style.borderStyle = 'dashed';
  700. }
  701. else if (this.strokewidth > 0)
  702. {
  703. node.style.borderStyle = 'solid';
  704. }
  705. node.style.borderWidth = Math.max(1, Math.ceil(this.strokewidth * this.scale)) + 'px';
  706. }
  707. else
  708. {
  709. node.style.borderWidth = '0px';
  710. }
  711. color = (this.outline) ? null : this.fill;
  712. if (color != null && color != mxConstants.NONE)
  713. {
  714. node.style.backgroundColor = color;
  715. node.style.backgroundImage = 'none';
  716. }
  717. else if (this.pointerEvents)
  718. {
  719. node.style.backgroundColor = 'transparent';
  720. }
  721. else if (document.documentMode == 8)
  722. {
  723. mxUtils.addTransparentBackgroundFilter(node);
  724. }
  725. else
  726. {
  727. this.setTransparentBackgroundImage(node);
  728. }
  729. };
  730. /**
  731. * Function: updateHtmlBounds
  732. *
  733. * Allow optimization by replacing VML with HTML.
  734. */
  735. mxShape.prototype.updateHtmlBounds = function(node)
  736. {
  737. var sw = (document.documentMode >= 9) ? 0 : Math.ceil(this.strokewidth * this.scale);
  738. node.style.borderWidth = Math.max(1, sw) + 'px';
  739. node.style.overflow = 'hidden';
  740. node.style.left = Math.round(this.bounds.x - sw / 2) + 'px';
  741. node.style.top = Math.round(this.bounds.y - sw / 2) + 'px';
  742. if (document.compatMode == 'CSS1Compat')
  743. {
  744. sw = -sw;
  745. }
  746. node.style.width = Math.round(Math.max(0, this.bounds.width + sw)) + 'px';
  747. node.style.height = Math.round(Math.max(0, this.bounds.height + sw)) + 'px';
  748. };
  749. /**
  750. * Function: destroyCanvas
  751. *
  752. * Destroys the given canvas which was used for drawing. This implementation
  753. * increments the reference counts on all shared gradients used in the canvas.
  754. */
  755. mxShape.prototype.destroyCanvas = function(canvas)
  756. {
  757. // Manages reference counts
  758. if (canvas instanceof mxSvgCanvas2D)
  759. {
  760. // Increments ref counts
  761. for (var key in canvas.gradients)
  762. {
  763. var gradient = canvas.gradients[key];
  764. if (gradient != null)
  765. {
  766. gradient.mxRefCount = (gradient.mxRefCount || 0) + 1;
  767. }
  768. }
  769. this.releaseSvgGradients(this.oldGradients);
  770. this.oldGradients = canvas.gradients;
  771. }
  772. };
  773. /**
  774. * Function: beforePaint
  775. *
  776. * Invoked before paint is called.
  777. */
  778. mxShape.prototype.beforePaint = function(c) { }
  779. /**
  780. * Function: afterPaint
  781. *
  782. * Invokes after paint was called.
  783. */
  784. mxShape.prototype.afterPaint = function(c) { }
  785. /**
  786. * Function: paint
  787. *
  788. * Generic rendering code.
  789. */
  790. mxShape.prototype.paint = function(c)
  791. {
  792. var strokeDrawn = false;
  793. if (c != null && this.outline)
  794. {
  795. var stroke = c.stroke;
  796. c.stroke = function()
  797. {
  798. strokeDrawn = true;
  799. stroke.apply(this, arguments);
  800. };
  801. var fillAndStroke = c.fillAndStroke;
  802. c.fillAndStroke = function()
  803. {
  804. strokeDrawn = true;
  805. fillAndStroke.apply(this, arguments);
  806. };
  807. }
  808. // Scale is passed-through to canvas
  809. var s = this.scale;
  810. var x = this.bounds.x / s;
  811. var y = this.bounds.y / s;
  812. var w = this.bounds.width / s;
  813. var h = this.bounds.height / s;
  814. if (this.isPaintBoundsInverted())
  815. {
  816. var t = (w - h) / 2;
  817. x += t;
  818. y -= t;
  819. var tmp = w;
  820. w = h;
  821. h = tmp;
  822. }
  823. this.updateTransform(c, x, y, w, h);
  824. this.configureCanvas(c, x, y, w, h);
  825. // Adds background rectangle to capture events
  826. var bg = null;
  827. if ((this.stencil == null && this.points == null && this.shapePointerEvents) ||
  828. (this.stencil != null && this.stencilPointerEvents))
  829. {
  830. var bb = this.createBoundingBox();
  831. if (this.dialect == mxConstants.DIALECT_SVG)
  832. {
  833. bg = this.createTransparentSvgRectangle(bb.x, bb.y, bb.width, bb.height);
  834. this.node.appendChild(bg);
  835. }
  836. else
  837. {
  838. var rect = c.createRect('rect', bb.x / s, bb.y / s, bb.width / s, bb.height / s);
  839. rect.appendChild(c.createTransparentFill());
  840. rect.stroked = 'false';
  841. c.root.appendChild(rect);
  842. }
  843. }
  844. if (this.stencil != null)
  845. {
  846. this.stencil.drawShape(c, this, x, y, w, h);
  847. }
  848. else
  849. {
  850. // Stencils have separate strokewidth
  851. c.setStrokeWidth(this.strokewidth);
  852. if (this.points != null)
  853. {
  854. // Paints edge shape
  855. var pts = [];
  856. for (var i = 0; i < this.points.length; i++)
  857. {
  858. if (this.points[i] != null)
  859. {
  860. pts.push(new mxPoint(this.points[i].x / s, this.points[i].y / s));
  861. }
  862. }
  863. this.paintEdgeShape(c, pts);
  864. }
  865. else
  866. {
  867. // Paints vertex shape
  868. this.paintVertexShape(c, x, y, w, h);
  869. }
  870. }
  871. if (bg != null && c.state != null && c.state.transform != null)
  872. {
  873. bg.setAttribute('transform', c.state.transform);
  874. }
  875. // Draws highlight rectangle if no stroke was used
  876. if (c != null && this.outline && !strokeDrawn)
  877. {
  878. c.rect(x, y, w, h);
  879. c.stroke();
  880. }
  881. };
  882. /**
  883. * Function: configureCanvas
  884. *
  885. * Sets the state of the canvas for drawing the shape.
  886. */
  887. mxShape.prototype.configureCanvas = function(c, x, y, w, h)
  888. {
  889. var dash = null;
  890. if (this.style != null)
  891. {
  892. dash = this.style['dashPattern'];
  893. }
  894. c.setAlpha(this.opacity / 100);
  895. c.setFillAlpha(this.fillOpacity / 100);
  896. c.setStrokeAlpha(this.strokeOpacity / 100);
  897. // Sets alpha, colors and gradients
  898. if (this.isShadow != null)
  899. {
  900. c.setShadow(this.isShadow);
  901. }
  902. // Dash pattern
  903. if (this.isDashed != null)
  904. {
  905. c.setDashed(this.isDashed, (this.style != null) ?
  906. mxUtils.getValue(this.style, mxConstants.STYLE_FIX_DASH, false) == 1 : false);
  907. }
  908. if (dash != null)
  909. {
  910. c.setDashPattern(dash);
  911. }
  912. if (this.fill != null && this.fill != mxConstants.NONE && this.gradient && this.gradient != mxConstants.NONE)
  913. {
  914. var b = this.getGradientBounds(c, x, y, w, h);
  915. c.setGradient(this.fill, this.gradient, b.x, b.y, b.width, b.height, this.gradientDirection);
  916. }
  917. else
  918. {
  919. c.setFillColor(this.fill);
  920. }
  921. c.setStrokeColor(this.stroke);
  922. };
  923. /**
  924. * Function: getGradientBounds
  925. *
  926. * Returns the bounding box for the gradient box for this shape.
  927. */
  928. mxShape.prototype.getGradientBounds = function(c, x, y, w, h)
  929. {
  930. return new mxRectangle(x, y, w, h);
  931. };
  932. /**
  933. * Function: updateTransform
  934. *
  935. * Sets the scale and rotation on the given canvas.
  936. */
  937. mxShape.prototype.updateTransform = function(c, x, y, w, h)
  938. {
  939. // NOTE: Currently, scale is implemented in state and canvas. This will
  940. // move to canvas in a later version, so that the states are unscaled
  941. // and untranslated and do not need an update after zooming or panning.
  942. c.scale(this.scale);
  943. c.rotate(this.getShapeRotation(), this.flipH, this.flipV, x + w / 2, y + h / 2);
  944. };
  945. /**
  946. * Function: paintVertexShape
  947. *
  948. * Paints the vertex shape.
  949. */
  950. mxShape.prototype.paintVertexShape = function(c, x, y, w, h)
  951. {
  952. this.paintBackground(c, x, y, w, h);
  953. if (!this.outline || this.style == null || mxUtils.getValue(
  954. this.style, mxConstants.STYLE_BACKGROUND_OUTLINE, 0) == 0)
  955. {
  956. c.setShadow(false);
  957. this.paintForeground(c, x, y, w, h);
  958. }
  959. };
  960. /**
  961. * Function: paintBackground
  962. *
  963. * Hook for subclassers. This implementation is empty.
  964. */
  965. mxShape.prototype.paintBackground = function(c, x, y, w, h) { };
  966. /**
  967. * Function: paintForeground
  968. *
  969. * Hook for subclassers. This implementation is empty.
  970. */
  971. mxShape.prototype.paintForeground = function(c, x, y, w, h) { };
  972. /**
  973. * Function: paintEdgeShape
  974. *
  975. * Hook for subclassers. This implementation is empty.
  976. */
  977. mxShape.prototype.paintEdgeShape = function(c, pts) { };
  978. /**
  979. * Function: getArcSize
  980. *
  981. * Returns the arc size for the given dimension.
  982. */
  983. mxShape.prototype.getArcSize = function(w, h)
  984. {
  985. var r = 0;
  986. if (mxUtils.getValue(this.style, mxConstants.STYLE_ABSOLUTE_ARCSIZE, 0) == '1')
  987. {
  988. r = Math.min(w / 2, Math.min(h / 2, mxUtils.getValue(this.style,
  989. mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2));
  990. }
  991. else
  992. {
  993. var f = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE,
  994. mxConstants.RECTANGLE_ROUNDING_FACTOR * 100) / 100;
  995. r = Math.min(w * f, h * f);
  996. }
  997. return r;
  998. };
  999. /**
  1000. * Function: paintGlassEffect
  1001. *
  1002. * Paints the glass gradient effect.
  1003. */
  1004. mxShape.prototype.paintGlassEffect = function(c, x, y, w, h, arc)
  1005. {
  1006. var sw = Math.ceil(this.strokewidth / 2);
  1007. var size = 0.4;
  1008. c.setGradient('#ffffff', '#ffffff', x, y, w, h * 0.6, 'south', 0.9, 0.1);
  1009. c.begin();
  1010. arc += 2 * sw;
  1011. if (this.isRounded)
  1012. {
  1013. c.moveTo(x - sw + arc, y - sw);
  1014. c.quadTo(x - sw, y - sw, x - sw, y - sw + arc);
  1015. c.lineTo(x - sw, y + h * size);
  1016. c.quadTo(x + w * 0.5, y + h * 0.7, x + w + sw, y + h * size);
  1017. c.lineTo(x + w + sw, y - sw + arc);
  1018. c.quadTo(x + w + sw, y - sw, x + w + sw - arc, y - sw);
  1019. }
  1020. else
  1021. {
  1022. c.moveTo(x - sw, y - sw);
  1023. c.lineTo(x - sw, y + h * size);
  1024. c.quadTo(x + w * 0.5, y + h * 0.7, x + w + sw, y + h * size);
  1025. c.lineTo(x + w + sw, y - sw);
  1026. }
  1027. c.close();
  1028. c.fill();
  1029. };
  1030. /**
  1031. * Function: addPoints
  1032. *
  1033. * Paints the given points with rounded corners.
  1034. */
  1035. mxShape.prototype.addPoints = function(c, pts, rounded, arcSize, close, exclude, initialMove)
  1036. {
  1037. if (pts != null && pts.length > 0)
  1038. {
  1039. initialMove = (initialMove != null) ? initialMove : true;
  1040. var pe = pts[pts.length - 1];
  1041. // Adds virtual waypoint in the center between start and end point
  1042. if (close && rounded)
  1043. {
  1044. pts = pts.slice();
  1045. var p0 = pts[0];
  1046. var wp = new mxPoint(pe.x + (p0.x - pe.x) / 2, pe.y + (p0.y - pe.y) / 2);
  1047. pts.splice(0, 0, wp);
  1048. }
  1049. var pt = pts[0];
  1050. var i = 1;
  1051. // Draws the line segments
  1052. if (initialMove)
  1053. {
  1054. c.moveTo(pt.x, pt.y);
  1055. }
  1056. else
  1057. {
  1058. c.lineTo(pt.x, pt.y);
  1059. }
  1060. while (i < ((close) ? pts.length : pts.length - 1))
  1061. {
  1062. var tmp = pts[mxUtils.mod(i, pts.length)];
  1063. var dx = pt.x - tmp.x;
  1064. var dy = pt.y - tmp.y;
  1065. if (rounded && (dx != 0 || dy != 0) && (exclude == null || mxUtils.indexOf(exclude, i - 1) < 0))
  1066. {
  1067. // Draws a line from the last point to the current
  1068. // point with a spacing of size off the current point
  1069. // into direction of the last point
  1070. var dist = Math.sqrt(dx * dx + dy * dy);
  1071. var nx1 = dx * Math.min(arcSize, dist / 2) / dist;
  1072. var ny1 = dy * Math.min(arcSize, dist / 2) / dist;
  1073. var x1 = tmp.x + nx1;
  1074. var y1 = tmp.y + ny1;
  1075. c.lineTo(x1, y1);
  1076. // Draws a curve from the last point to the current
  1077. // point with a spacing of size off the current point
  1078. // into direction of the next point
  1079. var next = pts[mxUtils.mod(i + 1, pts.length)];
  1080. // Uses next non-overlapping point
  1081. while (i < pts.length - 2 && Math.round(next.x - tmp.x) == 0 && Math.round(next.y - tmp.y) == 0)
  1082. {
  1083. next = pts[mxUtils.mod(i + 2, pts.length)];
  1084. i++;
  1085. }
  1086. dx = next.x - tmp.x;
  1087. dy = next.y - tmp.y;
  1088. dist = Math.max(1, Math.sqrt(dx * dx + dy * dy));
  1089. var nx2 = dx * Math.min(arcSize, dist / 2) / dist;
  1090. var ny2 = dy * Math.min(arcSize, dist / 2) / dist;
  1091. var x2 = tmp.x + nx2;
  1092. var y2 = tmp.y + ny2;
  1093. c.quadTo(tmp.x, tmp.y, x2, y2);
  1094. tmp = new mxPoint(x2, y2);
  1095. }
  1096. else
  1097. {
  1098. c.lineTo(tmp.x, tmp.y);
  1099. }
  1100. pt = tmp;
  1101. i++;
  1102. }
  1103. if (close)
  1104. {
  1105. c.close();
  1106. }
  1107. else
  1108. {
  1109. c.lineTo(pe.x, pe.y);
  1110. }
  1111. }
  1112. };
  1113. /**
  1114. * Function: resetStyles
  1115. *
  1116. * Resets all styles.
  1117. */
  1118. mxShape.prototype.resetStyles = function()
  1119. {
  1120. this.initStyles();
  1121. this.spacing = 0;
  1122. delete this.fill;
  1123. delete this.gradient;
  1124. delete this.gradientDirection;
  1125. delete this.stroke;
  1126. delete this.startSize;
  1127. delete this.endSize;
  1128. delete this.startArrow;
  1129. delete this.endArrow;
  1130. delete this.direction;
  1131. delete this.isShadow;
  1132. delete this.isDashed;
  1133. delete this.isRounded;
  1134. delete this.glass;
  1135. };
  1136. /**
  1137. * Function: apply
  1138. *
  1139. * Applies the style of the given <mxCellState> to the shape. This
  1140. * implementation assigns the following styles to local fields:
  1141. *
  1142. * - <mxConstants.STYLE_FILLCOLOR> => fill
  1143. * - <mxConstants.STYLE_GRADIENTCOLOR> => gradient
  1144. * - <mxConstants.STYLE_GRADIENT_DIRECTION> => gradientDirection
  1145. * - <mxConstants.STYLE_OPACITY> => opacity
  1146. * - <mxConstants.STYLE_FILL_OPACITY> => fillOpacity
  1147. * - <mxConstants.STYLE_STROKE_OPACITY> => strokeOpacity
  1148. * - <mxConstants.STYLE_STROKECOLOR> => stroke
  1149. * - <mxConstants.STYLE_STROKEWIDTH> => strokewidth
  1150. * - <mxConstants.STYLE_SHADOW> => isShadow
  1151. * - <mxConstants.STYLE_DASHED> => isDashed
  1152. * - <mxConstants.STYLE_SPACING> => spacing
  1153. * - <mxConstants.STYLE_STARTSIZE> => startSize
  1154. * - <mxConstants.STYLE_ENDSIZE> => endSize
  1155. * - <mxConstants.STYLE_ROUNDED> => isRounded
  1156. * - <mxConstants.STYLE_STARTARROW> => startArrow
  1157. * - <mxConstants.STYLE_ENDARROW> => endArrow
  1158. * - <mxConstants.STYLE_ROTATION> => rotation
  1159. * - <mxConstants.STYLE_DIRECTION> => direction
  1160. * - <mxConstants.STYLE_GLASS> => glass
  1161. *
  1162. * This keeps a reference to the <style>. If you need to keep a reference to
  1163. * the cell, you can override this method and store a local reference to
  1164. * state.cell or the <mxCellState> itself. If <outline> should be true, make
  1165. * sure to set it before calling this method.
  1166. *
  1167. * Parameters:
  1168. *
  1169. * state - <mxCellState> of the corresponding cell.
  1170. */
  1171. mxShape.prototype.apply = function(state)
  1172. {
  1173. this.state = state;
  1174. this.style = state.style;
  1175. if (this.style != null)
  1176. {
  1177. this.fill = mxUtils.getValue(this.style, mxConstants.STYLE_FILLCOLOR, this.fill);
  1178. this.gradient = mxUtils.getValue(this.style, mxConstants.STYLE_GRADIENTCOLOR, this.gradient);
  1179. this.gradientDirection = mxUtils.getValue(this.style, mxConstants.STYLE_GRADIENT_DIRECTION, this.gradientDirection);
  1180. this.opacity = mxUtils.getValue(this.style, mxConstants.STYLE_OPACITY, this.opacity);
  1181. this.fillOpacity = mxUtils.getValue(this.style, mxConstants.STYLE_FILL_OPACITY, this.fillOpacity);
  1182. this.strokeOpacity = mxUtils.getValue(this.style, mxConstants.STYLE_STROKE_OPACITY, this.strokeOpacity);
  1183. this.stroke = mxUtils.getValue(this.style, mxConstants.STYLE_STROKECOLOR, this.stroke);
  1184. this.strokewidth = mxUtils.getNumber(this.style, mxConstants.STYLE_STROKEWIDTH, this.strokewidth);
  1185. this.spacing = mxUtils.getValue(this.style, mxConstants.STYLE_SPACING, this.spacing);
  1186. this.startSize = mxUtils.getNumber(this.style, mxConstants.STYLE_STARTSIZE, this.startSize);
  1187. this.endSize = mxUtils.getNumber(this.style, mxConstants.STYLE_ENDSIZE, this.endSize);
  1188. this.startArrow = mxUtils.getValue(this.style, mxConstants.STYLE_STARTARROW, this.startArrow);
  1189. this.endArrow = mxUtils.getValue(this.style, mxConstants.STYLE_ENDARROW, this.endArrow);
  1190. this.rotation = mxUtils.getValue(this.style, mxConstants.STYLE_ROTATION, this.rotation);
  1191. this.direction = mxUtils.getValue(this.style, mxConstants.STYLE_DIRECTION, this.direction);
  1192. this.flipH = mxUtils.getValue(this.style, mxConstants.STYLE_FLIPH, 0) == 1;
  1193. this.flipV = mxUtils.getValue(this.style, mxConstants.STYLE_FLIPV, 0) == 1;
  1194. // Legacy support for stencilFlipH/V
  1195. if (this.stencil != null)
  1196. {
  1197. this.flipH = mxUtils.getValue(this.style, 'stencilFlipH', 0) == 1 || this.flipH;
  1198. this.flipV = mxUtils.getValue(this.style, 'stencilFlipV', 0) == 1 || this.flipV;
  1199. }
  1200. if (this.direction == mxConstants.DIRECTION_NORTH || this.direction == mxConstants.DIRECTION_SOUTH)
  1201. {
  1202. var tmp = this.flipH;
  1203. this.flipH = this.flipV;
  1204. this.flipV = tmp;
  1205. }
  1206. this.isShadow = mxUtils.getValue(this.style, mxConstants.STYLE_SHADOW, this.isShadow) == 1;
  1207. this.isDashed = mxUtils.getValue(this.style, mxConstants.STYLE_DASHED, this.isDashed) == 1;
  1208. this.isRounded = mxUtils.getValue(this.style, mxConstants.STYLE_ROUNDED, this.isRounded) == 1;
  1209. this.glass = mxUtils.getValue(this.style, mxConstants.STYLE_GLASS, this.glass) == 1;
  1210. if (this.fill == mxConstants.NONE)
  1211. {
  1212. this.fill = null;
  1213. }
  1214. if (this.gradient == mxConstants.NONE)
  1215. {
  1216. this.gradient = null;
  1217. }
  1218. if (this.stroke == mxConstants.NONE)
  1219. {
  1220. this.stroke = null;
  1221. }
  1222. }
  1223. };
  1224. /**
  1225. * Function: setCursor
  1226. *
  1227. * Sets the cursor on the given shape.
  1228. *
  1229. * Parameters:
  1230. *
  1231. * cursor - The cursor to be used.
  1232. */
  1233. mxShape.prototype.setCursor = function(cursor)
  1234. {
  1235. if (cursor == null)
  1236. {
  1237. cursor = '';
  1238. }
  1239. this.cursor = cursor;
  1240. if (this.node != null)
  1241. {
  1242. this.node.style.cursor = cursor;
  1243. }
  1244. };
  1245. /**
  1246. * Function: getCursor
  1247. *
  1248. * Returns the current cursor.
  1249. */
  1250. mxShape.prototype.getCursor = function()
  1251. {
  1252. return this.cursor;
  1253. };
  1254. /**
  1255. * Function: isRoundable
  1256. *
  1257. * Hook for subclassers.
  1258. */
  1259. mxShape.prototype.isRoundable = function()
  1260. {
  1261. return false;
  1262. };
  1263. /**
  1264. * Function: updateBoundingBox
  1265. *
  1266. * Updates the <boundingBox> for this shape using <createBoundingBox> and
  1267. * <augmentBoundingBox> and stores the result in <boundingBox>.
  1268. */
  1269. mxShape.prototype.updateBoundingBox = function()
  1270. {
  1271. // Tries to get bounding box from SVG subsystem
  1272. // LATER: Use getBoundingClientRect for fallback in VML
  1273. if (this.useSvgBoundingBox && this.node != null && this.node.ownerSVGElement != null)
  1274. {
  1275. try
  1276. {
  1277. var b = this.node.getBBox();
  1278. if (b.width > 0 && b.height > 0)
  1279. {
  1280. this.boundingBox = new mxRectangle(b.x, b.y, b.width, b.height);
  1281. // Adds strokeWidth
  1282. this.boundingBox.grow(this.strokewidth * this.scale / 2);
  1283. return;
  1284. }
  1285. }
  1286. catch(e)
  1287. {
  1288. // fallback to code below
  1289. }
  1290. }
  1291. if (this.bounds != null)
  1292. {
  1293. var bbox = this.createBoundingBox();
  1294. if (bbox != null)
  1295. {
  1296. this.augmentBoundingBox(bbox);
  1297. var rot = this.getShapeRotation();
  1298. if (rot != 0)
  1299. {
  1300. bbox = mxUtils.getBoundingBox(bbox, rot);
  1301. }
  1302. }
  1303. this.boundingBox = bbox;
  1304. }
  1305. };
  1306. /**
  1307. * Function: createBoundingBox
  1308. *
  1309. * Returns a new rectangle that represents the bounding box of the bare shape
  1310. * with no shadows or strokewidths.
  1311. */
  1312. mxShape.prototype.createBoundingBox = function()
  1313. {
  1314. var bb = this.bounds.clone();
  1315. if ((this.stencil != null && (this.direction == mxConstants.DIRECTION_NORTH ||
  1316. this.direction == mxConstants.DIRECTION_SOUTH)) || this.isPaintBoundsInverted())
  1317. {
  1318. bb.rotate90();
  1319. }
  1320. return bb;
  1321. };
  1322. /**
  1323. * Function: augmentBoundingBox
  1324. *
  1325. * Augments the bounding box with the strokewidth and shadow offsets.
  1326. */
  1327. mxShape.prototype.augmentBoundingBox = function(bbox)
  1328. {
  1329. if (this.isShadow)
  1330. {
  1331. bbox.width += Math.ceil(mxConstants.SHADOW_OFFSET_X * this.scale);
  1332. bbox.height += Math.ceil(mxConstants.SHADOW_OFFSET_Y * this.scale);
  1333. }
  1334. // Adds strokeWidth
  1335. bbox.grow(this.strokewidth * this.scale / 2);
  1336. };
  1337. /**
  1338. * Function: isPaintBoundsInverted
  1339. *
  1340. * Returns true if the bounds should be inverted.
  1341. */
  1342. mxShape.prototype.isPaintBoundsInverted = function()
  1343. {
  1344. // Stencil implements inversion via aspect
  1345. return this.stencil == null && (this.direction == mxConstants.DIRECTION_NORTH ||
  1346. this.direction == mxConstants.DIRECTION_SOUTH);
  1347. };
  1348. /**
  1349. * Function: getRotation
  1350. *
  1351. * Returns the rotation from the style.
  1352. */
  1353. mxShape.prototype.getRotation = function()
  1354. {
  1355. return (this.rotation != null) ? this.rotation : 0;
  1356. };
  1357. /**
  1358. * Function: getTextRotation
  1359. *
  1360. * Returns the rotation for the text label.
  1361. */
  1362. mxShape.prototype.getTextRotation = function()
  1363. {
  1364. var rot = this.getRotation();
  1365. if (mxUtils.getValue(this.style, mxConstants.STYLE_HORIZONTAL, 1) != 1)
  1366. {
  1367. rot += mxText.prototype.verticalTextRotation;
  1368. }
  1369. return rot;
  1370. };
  1371. /**
  1372. * Function: getShapeRotation
  1373. *
  1374. * Returns the actual rotation of the shape.
  1375. */
  1376. mxShape.prototype.getShapeRotation = function()
  1377. {
  1378. var rot = this.getRotation();
  1379. if (this.direction != null)
  1380. {
  1381. if (this.direction == mxConstants.DIRECTION_NORTH)
  1382. {
  1383. rot += 270;
  1384. }
  1385. else if (this.direction == mxConstants.DIRECTION_WEST)
  1386. {
  1387. rot += 180;
  1388. }
  1389. else if (this.direction == mxConstants.DIRECTION_SOUTH)
  1390. {
  1391. rot += 90;
  1392. }
  1393. }
  1394. return rot;
  1395. };
  1396. /**
  1397. * Function: createTransparentSvgRectangle
  1398. *
  1399. * Adds a transparent rectangle that catches all events.
  1400. */
  1401. mxShape.prototype.createTransparentSvgRectangle = function(x, y, w, h)
  1402. {
  1403. var rect = document.createElementNS(mxConstants.NS_SVG, 'rect');
  1404. rect.setAttribute('x', x);
  1405. rect.setAttribute('y', y);
  1406. rect.setAttribute('width', w);
  1407. rect.setAttribute('height', h);
  1408. rect.setAttribute('fill', 'none');
  1409. rect.setAttribute('stroke', 'none');
  1410. rect.setAttribute('pointer-events', 'all');
  1411. return rect;
  1412. };
  1413. /**
  1414. * Function: setTransparentBackgroundImage
  1415. *
  1416. * Sets a transparent background CSS style to catch all events.
  1417. *
  1418. * Paints the line shape.
  1419. */
  1420. mxShape.prototype.setTransparentBackgroundImage = function(node)
  1421. {
  1422. node.style.backgroundImage = 'url(\'' + mxClient.imageBasePath + '/transparent.gif\')';
  1423. };
  1424. /**
  1425. * Function: releaseSvgGradients
  1426. *
  1427. * Paints the line shape.
  1428. */
  1429. mxShape.prototype.releaseSvgGradients = function(grads)
  1430. {
  1431. if (grads != null)
  1432. {
  1433. for (var key in grads)
  1434. {
  1435. var gradient = grads[key];
  1436. if (gradient != null)
  1437. {
  1438. gradient.mxRefCount = (gradient.mxRefCount || 0) - 1;
  1439. if (gradient.mxRefCount == 0 && gradient.parentNode != null)
  1440. {
  1441. gradient.parentNode.removeChild(gradient);
  1442. }
  1443. }
  1444. }
  1445. }
  1446. };
  1447. /**
  1448. * Function: destroy
  1449. *
  1450. * Destroys the shape by removing it from the DOM and releasing the DOM
  1451. * node associated with the shape using <mxEvent.release>.
  1452. */
  1453. mxShape.prototype.destroy = function()
  1454. {
  1455. if (this.node != null)
  1456. {
  1457. mxEvent.release(this.node);
  1458. if (this.node.parentNode != null)
  1459. {
  1460. this.node.parentNode.removeChild(this.node);
  1461. }
  1462. this.node = null;
  1463. }
  1464. // Decrements refCount and removes unused
  1465. this.releaseSvgGradients(this.oldGradients);
  1466. this.oldGradients = null;
  1467. };
  1468. __mxOutput.mxShape = typeof mxShape !== 'undefined' ? mxShape : undefined;