mxText.js 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463
  1. /**
  2. * Copyright (c) 2006-2015, JGraph Ltd
  3. * Copyright (c) 2006-2015, Gaudenz Alder
  4. */
  5. /**
  6. * Class: mxText
  7. *
  8. * Extends <mxShape> to implement a text shape. To change vertical text from
  9. * bottom to top to top to bottom, the following code can be used:
  10. *
  11. * (code)
  12. * mxText.prototype.verticalTextRotation = 90;
  13. * (end)
  14. *
  15. * Constructor: mxText
  16. *
  17. * Constructs a new text shape.
  18. *
  19. * Parameters:
  20. *
  21. * value - String that represents the text to be displayed. This is stored in
  22. * <value>.
  23. * bounds - <mxRectangle> that defines the bounds. This is stored in
  24. * <mxShape.bounds>.
  25. * align - Specifies the horizontal alignment. Default is ''. This is stored in
  26. * <align>.
  27. * valign - Specifies the vertical alignment. Default is ''. This is stored in
  28. * <valign>.
  29. * color - String that specifies the text color. Default is 'black'. This is
  30. * stored in <color>.
  31. * family - String that specifies the font family. Default is
  32. * <mxConstants.DEFAULT_FONTFAMILY>. This is stored in <family>.
  33. * size - Integer that specifies the font size. Default is
  34. * <mxConstants.DEFAULT_FONTSIZE>. This is stored in <size>.
  35. * fontStyle - Specifies the font style. Default is 0. This is stored in
  36. * <fontStyle>.
  37. * spacing - Integer that specifies the global spacing. Default is 2. This is
  38. * stored in <spacing>.
  39. * spacingTop - Integer that specifies the top spacing. Default is 0. The
  40. * sum of the spacing and this is stored in <spacingTop>.
  41. * spacingRight - Integer that specifies the right spacing. Default is 0. The
  42. * sum of the spacing and this is stored in <spacingRight>.
  43. * spacingBottom - Integer that specifies the bottom spacing. Default is 0.The
  44. * sum of the spacing and this is stored in <spacingBottom>.
  45. * spacingLeft - Integer that specifies the left spacing. Default is 0. The
  46. * sum of the spacing and this is stored in <spacingLeft>.
  47. * horizontal - Boolean that specifies if the label is horizontal. Default is
  48. * true. This is stored in <horizontal>.
  49. * background - String that specifies the background color. Default is null.
  50. * This is stored in <background>.
  51. * border - String that specifies the label border color. Default is null.
  52. * This is stored in <border>.
  53. * wrap - Specifies if word-wrapping should be enabled. Default is false.
  54. * This is stored in <wrap>.
  55. * clipped - Specifies if the label should be clipped. Default is false.
  56. * This is stored in <clipped>.
  57. * overflow - Value of the overflow style. Default is 'visible'.
  58. */
  59. function mxText(value, bounds, align, valign, color,
  60. family, size, fontStyle, spacing, spacingTop, spacingRight,
  61. spacingBottom, spacingLeft, horizontal, background, border,
  62. wrap, clipped, overflow, labelPadding, textDirection)
  63. {
  64. mxShape.call(this);
  65. this.value = value;
  66. this.bounds = bounds;
  67. this.color = (color != null) ? color : 'black';
  68. this.align = (align != null) ? align : mxConstants.ALIGN_CENTER;
  69. this.valign = (valign != null) ? valign : mxConstants.ALIGN_MIDDLE;
  70. this.family = (family != null) ? family : mxConstants.DEFAULT_FONTFAMILY;
  71. this.size = (size != null) ? size : mxConstants.DEFAULT_FONTSIZE;
  72. this.fontStyle = (fontStyle != null) ? fontStyle : mxConstants.DEFAULT_FONTSTYLE;
  73. this.spacing = parseInt(spacing || 2);
  74. this.spacingTop = this.spacing + parseInt(spacingTop || 0);
  75. this.spacingRight = this.spacing + parseInt(spacingRight || 0);
  76. this.spacingBottom = this.spacing + parseInt(spacingBottom || 0);
  77. this.spacingLeft = this.spacing + parseInt(spacingLeft || 0);
  78. this.horizontal = (horizontal != null) ? horizontal : true;
  79. this.background = background;
  80. this.border = border;
  81. this.wrap = (wrap != null) ? wrap : false;
  82. this.clipped = (clipped != null) ? clipped : false;
  83. this.overflow = (overflow != null) ? overflow : 'visible';
  84. this.labelPadding = (labelPadding != null) ? labelPadding : 0;
  85. this.textDirection = textDirection;
  86. this.rotation = 0;
  87. this.updateMargin();
  88. };
  89. /**
  90. * Extends mxShape.
  91. */
  92. mxUtils.extend(mxText, mxShape);
  93. /**
  94. * Variable: baseSpacingTop
  95. *
  96. * Specifies the spacing to be added to the top spacing. Default is 0. Use the
  97. * value 5 here to get the same label positions as in mxGraph 1.x.
  98. */
  99. mxText.prototype.baseSpacingTop = 0;
  100. /**
  101. * Variable: baseSpacingBottom
  102. *
  103. * Specifies the spacing to be added to the bottom spacing. Default is 0. Use the
  104. * value 1 here to get the same label positions as in mxGraph 1.x.
  105. */
  106. mxText.prototype.baseSpacingBottom = 0;
  107. /**
  108. * Variable: baseSpacingLeft
  109. *
  110. * Specifies the spacing to be added to the left spacing. Default is 0.
  111. */
  112. mxText.prototype.baseSpacingLeft = 0;
  113. /**
  114. * Variable: baseSpacingRight
  115. *
  116. * Specifies the spacing to be added to the right spacing. Default is 0.
  117. */
  118. mxText.prototype.baseSpacingRight = 0;
  119. /**
  120. * Variable: replaceLinefeeds
  121. *
  122. * Specifies if linefeeds in HTML labels should be replaced with BR tags.
  123. * Default is true.
  124. */
  125. mxText.prototype.replaceLinefeeds = true;
  126. /**
  127. * Variable: verticalTextRotation
  128. *
  129. * Rotation for vertical text. Default is -90 (bottom to top).
  130. */
  131. mxText.prototype.verticalTextRotation = -90;
  132. /**
  133. * Variable: ignoreClippedStringSize
  134. *
  135. * Specifies if the string size should be measured in <updateBoundingBox> if
  136. * the label is clipped and the label position is center and middle. If this is
  137. * true, then the bounding box will be set to <bounds>. Default is true.
  138. * <ignoreStringSize> has precedence over this switch.
  139. */
  140. mxText.prototype.ignoreClippedStringSize = true;
  141. /**
  142. * Variable: ignoreStringSize
  143. *
  144. * Specifies if the actual string size should be measured. If disabled the
  145. * boundingBox will not ignore the actual size of the string, otherwise
  146. * <bounds> will be used instead. Default is false.
  147. */
  148. mxText.prototype.ignoreStringSize = false;
  149. /**
  150. * Variable: textWidthPadding
  151. *
  152. * Specifies the padding to be added to the text width for the bounding box.
  153. * This is needed to make sure no clipping is applied to borders. Default is 4
  154. * for IE 8 standards mode and 3 for all others.
  155. */
  156. mxText.prototype.textWidthPadding = (document.documentMode == 8 && !mxClient.IS_EM) ? 4 : 3;
  157. /**
  158. * Variable: lastValue
  159. *
  160. * Contains the last rendered text value. Used for caching.
  161. */
  162. mxText.prototype.lastValue = null;
  163. /**
  164. * Variable: cacheEnabled
  165. *
  166. * Specifies if caching for HTML labels should be enabled. Default is true.
  167. */
  168. mxText.prototype.cacheEnabled = true;
  169. /**
  170. * Function: isHtmlAllowed
  171. *
  172. * Returns true if HTML is allowed for this shape. This implementation returns
  173. * true if the browser is not in IE8 standards mode.
  174. */
  175. mxText.prototype.isHtmlAllowed = function()
  176. {
  177. return document.documentMode != 8 || mxClient.IS_EM;
  178. };
  179. /**
  180. * Function: getSvgScreenOffset
  181. *
  182. * Disables offset in IE9 for crisper image output.
  183. */
  184. mxText.prototype.getSvgScreenOffset = function()
  185. {
  186. return 0;
  187. };
  188. /**
  189. * Function: checkBounds
  190. *
  191. * Returns true if the bounds are not null and all of its variables are numeric.
  192. */
  193. mxText.prototype.checkBounds = function()
  194. {
  195. return (!isNaN(this.scale) && isFinite(this.scale) && this.scale > 0 &&
  196. this.bounds != null && !isNaN(this.bounds.x) && !isNaN(this.bounds.y) &&
  197. !isNaN(this.bounds.width) && !isNaN(this.bounds.height));
  198. };
  199. /**
  200. * Function: configurePointerEvents
  201. *
  202. * Configures the pointer events for the given canvas.
  203. */
  204. mxText.prototype.configurePointerEvents = function(c)
  205. {
  206. // do nothing
  207. };
  208. /**
  209. * Function: getActualTextDirection
  210. *
  211. * Returns the actual text direction.
  212. */
  213. mxText.prototype.getActualTextDirection = function()
  214. {
  215. var dir = this.textDirection;
  216. if (dir == mxConstants.TEXT_DIRECTION_AUTO)
  217. {
  218. dir = this.getAutoDirection();
  219. }
  220. if (dir != mxConstants.TEXT_DIRECTION_LTR &&
  221. dir != mxConstants.TEXT_DIRECTION_RTL &&
  222. dir != mxConstants.TEXT_DIRECTION_VERTICAL_LR &&
  223. dir != mxConstants.TEXT_DIRECTION_VERTICAL_RL)
  224. {
  225. dir = null;
  226. }
  227. return dir;
  228. };
  229. /**
  230. * Function: paint
  231. *
  232. * Generic rendering code.
  233. */
  234. mxText.prototype.paint = function(c, update)
  235. {
  236. // Scale is passed-through to canvas
  237. var s = this.scale;
  238. var x = this.bounds.x / s;
  239. var y = this.bounds.y / s;
  240. var w = this.bounds.width / s;
  241. var h = this.bounds.height / s;
  242. this.updateTransform(c, x, y, w, h);
  243. this.configureCanvas(c, x, y, w, h);
  244. this.updateSvgFilters((c != null) ? c.state.scale : s);
  245. var dir = this.getActualTextDirection();
  246. if (update)
  247. {
  248. c.updateText(x, y, w, h, this.align, this.valign, this.wrap, this.overflow,
  249. this.clipped, this.getTextRotation(), dir, this.node);
  250. }
  251. else
  252. {
  253. // Checks if text contains HTML markup
  254. var realHtml = mxUtils.isNode(this.value) || this.dialect == mxConstants.DIALECT_STRICTHTML;
  255. // Always renders labels as HTML in VML
  256. var fmt = (realHtml) ? 'html' : '';
  257. var val = this.value;
  258. if (!realHtml && fmt == 'html')
  259. {
  260. val = mxUtils.htmlEntities(val, false);
  261. }
  262. if (fmt == 'html' && !mxUtils.isNode(this.value))
  263. {
  264. val = mxUtils.replaceTrailingNewlines(val, '<div><br></div>');
  265. }
  266. // Handles trailing newlines to make sure they are visible in rendering output
  267. val = (!mxUtils.isNode(this.value) && this.replaceLinefeeds && fmt == 'html') ?
  268. val.replace(/\n/g, '<br/>') : val;
  269. c.text(x, y, w, h, val, this.align, this.valign, this.wrap, fmt,
  270. this.overflow, this.clipped, this.getTextRotation(), dir);
  271. }
  272. };
  273. /**
  274. * Function: redraw
  275. *
  276. * Renders the text using the given DOM nodes.
  277. */
  278. mxText.prototype.redraw = function()
  279. {
  280. if (this.visible && this.checkBounds() && this.cacheEnabled && this.lastValue == this.value &&
  281. (mxUtils.isNode(this.value) || this.dialect == mxConstants.DIALECT_STRICTHTML))
  282. {
  283. if (this.node.nodeName == 'DIV')
  284. {
  285. if (mxClient.IS_SVG)
  286. {
  287. this.redrawHtmlShapeWithCss3();
  288. }
  289. else
  290. {
  291. this.updateSize(this.node, (this.state == null || this.state.view.textDiv == null));
  292. if (mxClient.IS_IE && (document.documentMode == null || document.documentMode <= 8))
  293. {
  294. this.updateHtmlFilter();
  295. }
  296. else
  297. {
  298. this.updateHtmlTransform();
  299. }
  300. }
  301. }
  302. else
  303. {
  304. var canvas = this.createCanvas();
  305. if (canvas != null && canvas.updateText != null)
  306. {
  307. // Specifies if events should be handled
  308. canvas.pointerEvents = this.pointerEvents;
  309. this.paint(canvas, true);
  310. this.destroyCanvas(canvas);
  311. }
  312. else
  313. {
  314. // Fallback if canvas does not support updateText (VML)
  315. mxShape.prototype.redraw.apply(this, arguments);
  316. }
  317. }
  318. }
  319. else
  320. {
  321. mxShape.prototype.redraw.apply(this, arguments);
  322. if (mxUtils.isNode(this.value) || this.dialect == mxConstants.DIALECT_STRICTHTML)
  323. {
  324. this.lastValue = this.value;
  325. }
  326. else
  327. {
  328. this.lastValue = null;
  329. }
  330. }
  331. };
  332. /**
  333. * Function: resetStyles
  334. *
  335. * Resets all styles.
  336. */
  337. mxText.prototype.resetStyles = function()
  338. {
  339. mxShape.prototype.resetStyles.apply(this, arguments);
  340. this.color = 'black';
  341. this.align = mxConstants.ALIGN_CENTER;
  342. this.valign = mxConstants.ALIGN_MIDDLE;
  343. this.family = mxConstants.DEFAULT_FONTFAMILY;
  344. this.size = mxConstants.DEFAULT_FONTSIZE;
  345. this.fontStyle = mxConstants.DEFAULT_FONTSTYLE;
  346. this.spacing = 2;
  347. this.spacingTop = 2;
  348. this.spacingRight = 2;
  349. this.spacingBottom = 2;
  350. this.spacingLeft = 2;
  351. this.horizontal = true;
  352. delete this.background;
  353. delete this.border;
  354. this.textDirection = mxConstants.DEFAULT_TEXT_DIRECTION;
  355. delete this.margin;
  356. };
  357. /**
  358. * Function: apply
  359. *
  360. * Extends mxShape to update the text styles.
  361. *
  362. * Parameters:
  363. *
  364. * state - <mxCellState> of the corresponding cell.
  365. */
  366. mxText.prototype.apply = function(state)
  367. {
  368. var old = this.spacing;
  369. mxShape.prototype.apply.apply(this, arguments);
  370. if (this.style != null)
  371. {
  372. this.fontStyle = mxUtils.getValue(this.style, mxConstants.STYLE_FONTSTYLE, this.fontStyle);
  373. this.family = mxUtils.getValue(this.style, mxConstants.STYLE_FONTFAMILY, this.family);
  374. this.size = mxUtils.getValue(this.style, mxConstants.STYLE_FONTSIZE, this.size);
  375. this.color = mxUtils.getValue(this.style, mxConstants.STYLE_FONTCOLOR, this.color);
  376. this.align = mxUtils.getValue(this.style, mxConstants.STYLE_ALIGN, this.align);
  377. this.valign = mxUtils.getValue(this.style, mxConstants.STYLE_VERTICAL_ALIGN, this.valign);
  378. this.spacing = parseInt(mxUtils.getValue(this.style, mxConstants.STYLE_SPACING, this.spacing));
  379. this.spacingTop = parseInt(mxUtils.getValue(this.style, mxConstants.STYLE_SPACING_TOP, this.spacingTop - old)) + this.spacing;
  380. this.spacingRight = parseInt(mxUtils.getValue(this.style, mxConstants.STYLE_SPACING_RIGHT, this.spacingRight - old)) + this.spacing;
  381. this.spacingBottom = parseInt(mxUtils.getValue(this.style, mxConstants.STYLE_SPACING_BOTTOM, this.spacingBottom - old)) + this.spacing;
  382. this.spacingLeft = parseInt(mxUtils.getValue(this.style, mxConstants.STYLE_SPACING_LEFT, this.spacingLeft - old)) + this.spacing;
  383. this.horizontal = mxUtils.getValue(this.style, mxConstants.STYLE_HORIZONTAL, this.horizontal);
  384. this.background = mxUtils.getValue(this.style, mxConstants.STYLE_LABEL_BACKGROUNDCOLOR, this.background);
  385. this.border = mxUtils.getValue(this.style, mxConstants.STYLE_LABEL_BORDERCOLOR, this.border);
  386. this.textDirection = mxUtils.getValue(this.style, mxConstants.STYLE_TEXT_DIRECTION, mxConstants.DEFAULT_TEXT_DIRECTION);
  387. this.opacity = mxUtils.getValue(this.style, mxConstants.STYLE_TEXT_OPACITY, 100);
  388. this.updateMargin();
  389. }
  390. this.flipV = null;
  391. this.flipH = null;
  392. };
  393. /**
  394. * Function: getAutoDirection
  395. *
  396. * Used to determine the automatic text direction. Returns
  397. * <mxConstants.TEXT_DIRECTION_LTR> or <mxConstants.TEXT_DIRECTION_RTL>
  398. * depending on the contents of <value>. This is not invoked for HTML, wrapped
  399. * content or if <value> is a DOM node.
  400. */
  401. mxText.prototype.getAutoDirection = function()
  402. {
  403. // Looks for strong (directional) characters
  404. var tmp = /[A-Za-z\u05d0-\u065f\u066a-\u06ef\u06fa-\u07ff\ufb1d-\ufdff\ufe70-\ufefc]/.exec(this.value);
  405. // Returns the direction defined by the character
  406. return (tmp != null && tmp.length > 0 && tmp[0] > 'z') ?
  407. mxConstants.TEXT_DIRECTION_RTL : mxConstants.TEXT_DIRECTION_LTR;
  408. };
  409. /**
  410. * Function: getContentNode
  411. *
  412. * Returns the node that contains the rendered input.
  413. */
  414. mxText.prototype.getContentNode = function()
  415. {
  416. var result = this.node;
  417. if (result != null)
  418. {
  419. // Rendered with no foreignObject
  420. if (result.ownerSVGElement == null)
  421. {
  422. result = this.node.firstChild.firstChild;
  423. }
  424. else
  425. {
  426. // Innermost DIV that contains the actual content
  427. result = result.firstChild.firstChild.firstChild.firstChild.firstChild;
  428. }
  429. }
  430. return result;
  431. };
  432. /**
  433. * Function: updateBoundingBox
  434. *
  435. * Updates the <boundingBox> for this shape using the given node and position.
  436. */
  437. mxText.prototype.updateBoundingBox = function()
  438. {
  439. var node = this.node;
  440. this.boundingBox = this.bounds.clone();
  441. var rot = this.getTextRotation();
  442. var h = (this.style != null) ? mxUtils.getValue(this.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER) : null;
  443. var v = (this.style != null) ? mxUtils.getValue(this.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE) : null;
  444. if (!this.ignoreStringSize && node != null && this.overflow != 'fill' && (!this.clipped ||
  445. !this.ignoreClippedStringSize || h != mxConstants.ALIGN_CENTER || v != mxConstants.ALIGN_MIDDLE))
  446. {
  447. var ow = null;
  448. var oh = null;
  449. if (node.ownerSVGElement != null)
  450. {
  451. if (node.firstChild != null && node.firstChild.firstChild != null &&
  452. node.firstChild.firstChild.nodeName == 'foreignObject' &&
  453. node.firstChild.firstChild.firstChild != null &&
  454. node.firstChild.firstChild.firstChild.firstChild != null)
  455. {
  456. // Uses second inner DIV for font metrics
  457. node = node.firstChild.firstChild.firstChild.firstChild;
  458. oh = node.offsetHeight * this.scale;
  459. if (this.overflow == 'width')
  460. {
  461. ow = this.boundingBox.width;
  462. }
  463. else
  464. {
  465. ow = node.offsetWidth * this.scale;
  466. }
  467. }
  468. else
  469. {
  470. try
  471. {
  472. var b = node.getBBox();
  473. // Workaround for bounding box of empty string
  474. if (typeof(this.value) == 'string' && mxUtils.trim(this.value) == 0)
  475. {
  476. this.boundingBox = null;
  477. }
  478. else if (b.width == 0 && b.height == 0)
  479. {
  480. this.boundingBox = null;
  481. }
  482. else
  483. {
  484. this.boundingBox = new mxRectangle(b.x, b.y, b.width, b.height);
  485. }
  486. return;
  487. }
  488. catch (e)
  489. {
  490. // Ignores NS_ERROR_FAILURE in FF if container display is none.
  491. }
  492. }
  493. }
  494. else
  495. {
  496. var td = (this.state != null) ? this.state.view.textDiv : null;
  497. // Use cached offset size
  498. if (this.offsetWidth != null && this.offsetHeight != null)
  499. {
  500. ow = this.offsetWidth * this.scale;
  501. oh = this.offsetHeight * this.scale;
  502. }
  503. else
  504. {
  505. // Cannot get node size while container hidden so a
  506. // shared temporary DIV is used for text measuring
  507. if (td != null)
  508. {
  509. this.updateFont(td);
  510. this.updateSize(td, false);
  511. this.updateInnerHtml(td);
  512. node = td;
  513. }
  514. var sizeDiv = node;
  515. if (document.documentMode == 8 && !mxClient.IS_EM)
  516. {
  517. var w = Math.round(this.bounds.width / this.scale);
  518. if (this.wrap && w > 0)
  519. {
  520. node.style.wordWrap = mxConstants.WORD_WRAP;
  521. node.style.whiteSpace = 'normal';
  522. if (node.style.wordWrap != 'break-word')
  523. {
  524. // Innermost DIV is used for measuring text
  525. var divs = sizeDiv.getElementsByTagName('div');
  526. if (divs.length > 0)
  527. {
  528. sizeDiv = divs[divs.length - 1];
  529. }
  530. ow = sizeDiv.offsetWidth + 2;
  531. divs = this.node.getElementsByTagName('div');
  532. if (this.clipped)
  533. {
  534. ow = Math.min(w, ow);
  535. }
  536. // Second last DIV width must be updated in DOM tree
  537. if (divs.length > 1)
  538. {
  539. divs[divs.length - 2].style.width = ow + 'px';
  540. }
  541. }
  542. }
  543. else
  544. {
  545. node.style.whiteSpace = 'nowrap';
  546. }
  547. }
  548. else if (sizeDiv.firstChild != null && sizeDiv.firstChild.nodeName == 'DIV')
  549. {
  550. sizeDiv = sizeDiv.firstChild;
  551. }
  552. this.offsetWidth = sizeDiv.offsetWidth + this.textWidthPadding;
  553. this.offsetHeight = sizeDiv.offsetHeight;
  554. ow = this.offsetWidth * this.scale;
  555. oh = this.offsetHeight * this.scale;
  556. }
  557. }
  558. if (ow != null && oh != null)
  559. {
  560. this.boundingBox = new mxRectangle(this.bounds.x,
  561. this.bounds.y, ow, oh);
  562. }
  563. }
  564. if (this.boundingBox != null)
  565. {
  566. if (rot != 0)
  567. {
  568. // Accounts for pre-rotated x and y
  569. var bbox = mxUtils.getBoundingBox(new mxRectangle(
  570. this.margin.x * this.boundingBox.width,
  571. this.margin.y * this.boundingBox.height,
  572. this.boundingBox.width, this.boundingBox.height),
  573. rot, new mxPoint(0, 0));
  574. this.unrotatedBoundingBox = mxRectangle.fromRectangle(this.boundingBox);
  575. this.unrotatedBoundingBox.x += this.margin.x * this.unrotatedBoundingBox.width;
  576. this.unrotatedBoundingBox.y += this.margin.y * this.unrotatedBoundingBox.height;
  577. this.boundingBox.x += bbox.x;
  578. this.boundingBox.y += bbox.y;
  579. this.boundingBox.width = bbox.width;
  580. this.boundingBox.height = bbox.height;
  581. }
  582. else
  583. {
  584. this.boundingBox.x += this.margin.x * this.boundingBox.width;
  585. this.boundingBox.y += this.margin.y * this.boundingBox.height;
  586. this.unrotatedBoundingBox = null;
  587. }
  588. }
  589. };
  590. /**
  591. * Function: getShapeRotation
  592. *
  593. * Returns 0 to avoid using rotation in the canvas via updateTransform.
  594. */
  595. mxText.prototype.getShapeRotation = function()
  596. {
  597. return 0;
  598. };
  599. /**
  600. * Function: getTextRotation
  601. *
  602. * Returns the rotation for the text label of the corresponding shape.
  603. */
  604. mxText.prototype.getTextRotation = function()
  605. {
  606. return (this.state != null && this.state.shape != null) ? this.state.shape.getTextRotation() : 0;
  607. };
  608. /**
  609. * Function: isPaintBoundsInverted
  610. *
  611. * Inverts the bounds if <mxShape.isBoundsInverted> returns true or if the
  612. * horizontal style is false.
  613. */
  614. mxText.prototype.isPaintBoundsInverted = function()
  615. {
  616. return !this.horizontal && this.state != null && this.state.view.graph.model.isVertex(this.state.cell);
  617. };
  618. /**
  619. * Function: configureCanvas
  620. *
  621. * Sets the state of the canvas for drawing the shape.
  622. */
  623. mxText.prototype.configureCanvas = function(c, x, y, w, h)
  624. {
  625. mxShape.prototype.configureCanvas.apply(this, arguments);
  626. c.setFontColor(this.color);
  627. c.setFontBackgroundColor(this.background);
  628. c.setFontBorderColor(this.border);
  629. c.setFontFamily(this.family);
  630. c.setFontSize(this.size);
  631. c.setFontStyle(this.fontStyle);
  632. };
  633. /**
  634. * Function: isShadowEnabled
  635. *
  636. * Removes all child nodes and resets all CSS.
  637. */
  638. mxText.prototype.isShadowEnabled = function()
  639. {
  640. return (this.style != null) ? mxUtils.getValue(this.style,
  641. mxConstants.STYLE_TEXT_SHADOW, false) : false;
  642. };
  643. /**
  644. * Function: getHtmlValue
  645. *
  646. * Private helper function to create SVG elements
  647. */
  648. mxText.prototype.getHtmlValue = function()
  649. {
  650. var val = this.value;
  651. if (this.dialect != mxConstants.DIALECT_STRICTHTML)
  652. {
  653. val = mxUtils.htmlEntities(val, false);
  654. }
  655. // Handles trailing newlines to make sure they are visible in rendering output
  656. val = mxUtils.replaceTrailingNewlines(val, '<div><br></div>');
  657. val = (this.replaceLinefeeds) ? val.replace(/\n/g, '<br/>') : val;
  658. return val;
  659. };
  660. /**
  661. * Function: getTextCss
  662. *
  663. * Private helper function to create SVG elements
  664. */
  665. mxText.prototype.getTextCss = function()
  666. {
  667. var lh = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? (this.size * mxConstants.LINE_HEIGHT) + 'px' :
  668. mxConstants.LINE_HEIGHT;
  669. var css = 'display: inline-block; font-size: ' + this.size + 'px; ' +
  670. 'font-family: ' + this.family + '; color: ' + this.color + '; line-height: ' + lh +
  671. '; pointer-events: ' + ((this.pointerEvents) ? 'all' : 'none') + '; ';
  672. if ((this.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD)
  673. {
  674. css += 'font-weight: bold; ';
  675. }
  676. if ((this.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC)
  677. {
  678. css += 'font-style: italic; ';
  679. }
  680. var deco = [];
  681. if ((this.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE)
  682. {
  683. deco.push('underline');
  684. }
  685. if ((this.fontStyle & mxConstants.FONT_STRIKETHROUGH) == mxConstants.FONT_STRIKETHROUGH)
  686. {
  687. deco.push('line-through');
  688. }
  689. if (deco.length > 0)
  690. {
  691. css += 'text-decoration: ' + deco.join(' ') + '; ';
  692. }
  693. return css;
  694. };
  695. /**
  696. * Function: redrawHtmlShape
  697. *
  698. * Updates the HTML node(s) to reflect the latest bounds and scale.
  699. */
  700. mxText.prototype.redrawHtmlShape = function()
  701. {
  702. if (mxClient.IS_SVG)
  703. {
  704. this.redrawHtmlShapeWithCss3();
  705. }
  706. else
  707. {
  708. var style = this.node.style;
  709. // Resets CSS styles
  710. style.whiteSpace = 'normal';
  711. style.overflow = '';
  712. style.width = '';
  713. style.height = '';
  714. this.updateValue();
  715. this.updateFont(this.node);
  716. this.updateSize(this.node, (this.state == null || this.state.view.textDiv == null));
  717. this.offsetWidth = null;
  718. this.offsetHeight = null;
  719. if (mxClient.IS_IE && (document.documentMode == null || document.documentMode <= 8))
  720. {
  721. this.updateHtmlFilter();
  722. }
  723. else
  724. {
  725. this.updateHtmlTransform();
  726. }
  727. }
  728. };
  729. /**
  730. * Function: redrawHtmlShapeWithCss3
  731. *
  732. * Updates the HTML node(s) to reflect the latest bounds and scale.
  733. */
  734. mxText.prototype.redrawHtmlShapeWithCss3 = function()
  735. {
  736. var w = Math.max(0, Math.round(this.bounds.width / this.scale));
  737. var h = Math.max(0, Math.round(this.bounds.height / this.scale));
  738. var flex = 'position: absolute; left: ' + Math.round(this.bounds.x) + 'px; ' +
  739. 'top: ' + Math.round(this.bounds.y) + 'px; pointer-events: none; ';
  740. var block = this.getTextCss();
  741. var dir = this.getActualTextDirection();
  742. mxSvgCanvas2D.createCss(w + 2, h, this.align, this.valign, this.wrap, this.overflow, this.clipped, dir,
  743. (this.background != null) ? mxUtils.htmlEntities(this.background) : null,
  744. (this.border != null) ? mxUtils.htmlEntities(this.border) : null,
  745. flex, block, this.scale, mxUtils.bind(this, function(dx, dy, flex, item, block, ofl)
  746. {
  747. var r = this.getTextRotation();
  748. var tr = ((this.scale != 1) ? 'scale(' + this.scale + ') ' : '') +
  749. ((r != 0) ? 'rotate(' + r + 'deg) ' : '') +
  750. ((this.margin.x != 0 || this.margin.y != 0) ?
  751. 'translate(' + (this.margin.x * 100) + '%,' +
  752. (this.margin.y * 100) + '%)' : '');
  753. if (tr != '')
  754. {
  755. tr = 'transform-origin: 0 0; transform: ' + tr + '; ';
  756. }
  757. if (this.overflow == 'block' && this.valign == mxConstants.ALIGN_MIDDLE)
  758. {
  759. tr += 'max-height: ' + (h + 1) + 'px;';
  760. }
  761. if (ofl == '')
  762. {
  763. flex += item;
  764. item = 'display:inline-block; min-width: 100%; ' + tr;
  765. }
  766. else
  767. {
  768. item += tr;
  769. if (mxClient.IS_SF)
  770. {
  771. item += '-webkit-clip-path: content-box;';
  772. }
  773. }
  774. if (this.overflow == 'block')
  775. {
  776. item += 'width: 100%; ';
  777. }
  778. if (this.opacity < 100)
  779. {
  780. block += 'opacity: ' + (this.opacity / 100) + '; ';
  781. }
  782. this.node.setAttribute('style', flex);
  783. var html = (mxUtils.isNode(this.value)) ? this.value.outerHTML : this.getHtmlValue();
  784. if (this.node.firstChild == null)
  785. {
  786. this.node.innerHTML = '<div><div>' + html +'</div></div>';
  787. if (mxClient.IS_IE11)
  788. {
  789. this.fixFlexboxForIe11(this.node);
  790. }
  791. }
  792. this.node.firstChild.firstChild.setAttribute('style', block);
  793. this.node.firstChild.setAttribute('style', item);
  794. }));
  795. };
  796. /**
  797. * Function: fixFlexboxForIe11
  798. *
  799. * Rewrites flexbox CSS for IE11 to work around overflow issues.
  800. */
  801. mxText.prototype.fixFlexboxForIe11 = function(node)
  802. {
  803. var elts = node.querySelectorAll('div[style*="display: flex; justify-content: flex-end;"]');
  804. for (var i = 0; i < elts.length; i++)
  805. {
  806. // Fixes right aligned elements to allow for overflow
  807. elts[i].style.justifyContent = 'flex-start';
  808. elts[i].style.flexDirection = 'row-reverse';
  809. }
  810. // LATER: Overflow center with flexbox in IE11 that keeps word wrapping
  811. if (!this.wrap)
  812. {
  813. var elts = node.querySelectorAll('div[style*="display: flex; justify-content: center;"]');
  814. var w = -window.innerWidth;
  815. for (var i = 0; i < elts.length; i++)
  816. {
  817. elts[i].style.marginLeft = w + 'px';
  818. elts[i].style.marginRight = w + 'px';
  819. }
  820. }
  821. };
  822. /**
  823. * Function: updateHtmlTransform
  824. *
  825. * Returns the spacing as an <mxPoint>.
  826. */
  827. mxText.prototype.updateHtmlTransform = function()
  828. {
  829. var theta = this.getTextRotation();
  830. var style = this.node.style;
  831. var dx = this.margin.x;
  832. var dy = this.margin.y;
  833. if (theta != 0)
  834. {
  835. mxUtils.setPrefixedStyle(style, 'transformOrigin', (-dx * 100) + '%' + ' ' + (-dy * 100) + '%');
  836. mxUtils.setPrefixedStyle(style, 'transform', 'translate(' + (dx * 100) + '%' + ',' + (dy * 100) + '%) ' +
  837. 'scale(' + this.scale + ') rotate(' + theta + 'deg)');
  838. }
  839. else
  840. {
  841. mxUtils.setPrefixedStyle(style, 'transformOrigin', '0% 0%');
  842. mxUtils.setPrefixedStyle(style, 'transform', 'scale(' + this.scale + ') ' +
  843. 'translate(' + (dx * 100) + '%' + ',' + (dy * 100) + '%)');
  844. }
  845. style.left = Math.round(this.bounds.x - Math.ceil(dx * ((this.overflow != 'fill' &&
  846. this.overflow != 'width') ? 3 : 1))) + 'px';
  847. style.top = Math.round(this.bounds.y - dy * ((this.overflow != 'fill') ? 3 : 1)) + 'px';
  848. if (this.opacity < 100)
  849. {
  850. style.opacity = this.opacity / 100;
  851. }
  852. else
  853. {
  854. style.opacity = '';
  855. }
  856. };
  857. /**
  858. * Function: updateInnerHtml
  859. *
  860. * Sets the inner HTML of the given element to the <value>.
  861. */
  862. mxText.prototype.updateInnerHtml = function(elt)
  863. {
  864. if (mxUtils.isNode(this.value))
  865. {
  866. elt.innerHTML = this.value.outerHTML;
  867. }
  868. else
  869. {
  870. var val = this.value;
  871. if (this.dialect != mxConstants.DIALECT_STRICTHTML)
  872. {
  873. // LATER: Can be cached in updateValue
  874. val = mxUtils.htmlEntities(val, false);
  875. }
  876. // Handles trailing newlines to make sure they are visible in rendering output
  877. val = mxUtils.replaceTrailingNewlines(val, '<div>&nbsp;</div>');
  878. val = (this.replaceLinefeeds) ? val.replace(/\n/g, '<br/>') : val;
  879. val = '<div style="display:inline-block;_display:inline;">' + val + '</div>';
  880. elt.innerHTML = val;
  881. }
  882. };
  883. /**
  884. * Function: updateHtmlFilter
  885. *
  886. * Rotated text rendering quality is bad for IE9 quirks/IE8 standards
  887. */
  888. mxText.prototype.updateHtmlFilter = function()
  889. {
  890. var style = this.node.style;
  891. var dx = this.margin.x;
  892. var dy = this.margin.y;
  893. var s = this.scale;
  894. // Resets filter before getting offsetWidth
  895. mxUtils.setOpacity(this.node, this.opacity);
  896. // Adds 1 to match table height in 1.x
  897. var ow = 0;
  898. var oh = 0;
  899. var td = (this.state != null) ? this.state.view.textDiv : null;
  900. var sizeDiv = this.node;
  901. // Fallback for hidden text rendering in IE quirks mode
  902. if (td != null)
  903. {
  904. td.style.overflow = '';
  905. td.style.height = '';
  906. td.style.width = '';
  907. this.updateFont(td);
  908. this.updateSize(td, false);
  909. this.updateInnerHtml(td);
  910. var w = Math.round(this.bounds.width / this.scale);
  911. if (this.wrap && w > 0)
  912. {
  913. td.style.whiteSpace = 'normal';
  914. td.style.wordWrap = mxConstants.WORD_WRAP;
  915. ow = w;
  916. if (this.clipped)
  917. {
  918. ow = Math.min(ow, this.bounds.width);
  919. }
  920. td.style.width = ow + 'px';
  921. }
  922. else
  923. {
  924. td.style.whiteSpace = 'nowrap';
  925. }
  926. sizeDiv = td;
  927. if (sizeDiv.firstChild != null && sizeDiv.firstChild.nodeName == 'DIV')
  928. {
  929. sizeDiv = sizeDiv.firstChild;
  930. if (this.wrap && td.style.wordWrap == 'break-word')
  931. {
  932. sizeDiv.style.width = '100%';
  933. }
  934. }
  935. // Required to update the height of the text box after wrapping width is known
  936. if (!this.clipped && this.wrap && w > 0)
  937. {
  938. ow = sizeDiv.offsetWidth + this.textWidthPadding;
  939. td.style.width = ow + 'px';
  940. }
  941. oh = sizeDiv.offsetHeight + 2;
  942. }
  943. else if (sizeDiv.firstChild != null && sizeDiv.firstChild.nodeName == 'DIV')
  944. {
  945. sizeDiv = sizeDiv.firstChild;
  946. oh = sizeDiv.offsetHeight;
  947. }
  948. ow = sizeDiv.offsetWidth + this.textWidthPadding;
  949. if (this.clipped)
  950. {
  951. oh = Math.min(oh, this.bounds.height);
  952. }
  953. var w = this.bounds.width / s;
  954. var h = this.bounds.height / s;
  955. // Handles special case for live preview with no wrapper DIV and no textDiv
  956. if (this.overflow == 'fill')
  957. {
  958. oh = h;
  959. ow = w;
  960. }
  961. else if (this.overflow == 'width')
  962. {
  963. oh = sizeDiv.scrollHeight;
  964. ow = w;
  965. }
  966. // Stores for later use
  967. this.offsetWidth = ow;
  968. this.offsetHeight = oh;
  969. h = oh;
  970. if (this.overflow != 'fill' && this.overflow != 'width')
  971. {
  972. if (this.clipped)
  973. {
  974. ow = Math.min(w, ow);
  975. }
  976. w = ow;
  977. // Simulates max-width CSS in quirks mode
  978. if (this.wrap)
  979. {
  980. style.width = Math.round(w) + 'px';
  981. }
  982. }
  983. h *= s;
  984. w *= s;
  985. // Rotation case is handled via VML canvas
  986. var rad = this.getTextRotation() * (Math.PI / 180);
  987. // Precalculate cos and sin for the rotation
  988. var real_cos = parseFloat(parseFloat(Math.cos(rad)).toFixed(8));
  989. var real_sin = parseFloat(parseFloat(Math.sin(-rad)).toFixed(8));
  990. rad %= 2 * Math.PI;
  991. if (rad < 0)
  992. {
  993. rad += 2 * Math.PI;
  994. }
  995. rad %= Math.PI;
  996. if (rad > Math.PI / 2)
  997. {
  998. rad = Math.PI - rad;
  999. }
  1000. var cos = Math.cos(rad);
  1001. var sin = Math.sin(-rad);
  1002. var tx = w * -(dx + 0.5);
  1003. var ty = h * -(dy + 0.5);
  1004. var top_fix = (h - h * cos + w * sin) / 2 + real_sin * tx - real_cos * ty;
  1005. var left_fix = (w - w * cos + h * sin) / 2 - real_cos * tx - real_sin * ty;
  1006. if (rad != 0)
  1007. {
  1008. var f = 'progid:DXImageTransform.Microsoft.Matrix(M11=' + real_cos + ', M12='+
  1009. real_sin + ', M21=' + (-real_sin) + ', M22=' + real_cos + ', sizingMethod=\'auto expand\')';
  1010. if (style.filter != null && style.filter.length > 0)
  1011. {
  1012. style.filter += ' ' + f;
  1013. }
  1014. else
  1015. {
  1016. style.filter = f;
  1017. }
  1018. }
  1019. // Workaround for rendering offsets
  1020. var dy = 0;
  1021. style.zoom = s;
  1022. style.left = Math.round(this.bounds.x + left_fix - w / 2) + 'px';
  1023. style.top = Math.round(this.bounds.y + top_fix - h / 2 + dy) + 'px';
  1024. };
  1025. /**
  1026. * Function: updateValue
  1027. *
  1028. * Updates the HTML node(s) to reflect the latest bounds and scale.
  1029. */
  1030. mxText.prototype.updateValue = function()
  1031. {
  1032. if (mxUtils.isNode(this.value))
  1033. {
  1034. this.node.innerText = '';
  1035. this.node.appendChild(this.value);
  1036. }
  1037. else
  1038. {
  1039. var val = this.value;
  1040. if (this.dialect != mxConstants.DIALECT_STRICTHTML)
  1041. {
  1042. val = mxUtils.htmlEntities(val, false);
  1043. }
  1044. // Handles trailing newlines to make sure they are visible in rendering output
  1045. val = mxUtils.replaceTrailingNewlines(val, '<div><br></div>');
  1046. val = (this.replaceLinefeeds) ? val.replace(/\n/g, '<br/>') : val;
  1047. var bg = (this.background != null && this.background != mxConstants.NONE) ? this.background : null;
  1048. var bd = (this.border != null && this.border != mxConstants.NONE) ? this.border : null;
  1049. if (this.overflow == 'fill' || this.overflow == 'width')
  1050. {
  1051. if (bg != null)
  1052. {
  1053. this.node.style.backgroundColor = bg;
  1054. }
  1055. if (bd != null)
  1056. {
  1057. this.node.style.border = '1px solid ' + bd;
  1058. }
  1059. }
  1060. else
  1061. {
  1062. var css = '';
  1063. if (bg != null)
  1064. {
  1065. css += 'background-color:' + mxUtils.htmlEntities(bg) + ';';
  1066. }
  1067. if (bd != null)
  1068. {
  1069. css += 'border:1px solid ' + mxUtils.htmlEntities(bd) + ';';
  1070. }
  1071. // Wrapper DIV for background, zoom needed for inline in quirks
  1072. // and to measure wrapped font sizes in all browsers
  1073. // FIXME: Background size in quirks mode for wrapped text
  1074. var lh = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? (this.size * mxConstants.LINE_HEIGHT) + 'px' :
  1075. mxConstants.LINE_HEIGHT;
  1076. val = '<div style="zoom:1;' + css + 'display:inline-block;_display:inline;text-decoration:inherit;' +
  1077. 'padding-bottom:1px;padding-right:1px;line-height:' + lh + '">' + val + '</div>';
  1078. }
  1079. this.node.innerHTML = val;
  1080. // Sets text direction
  1081. var divs = this.node.getElementsByTagName('div');
  1082. if (divs.length > 0)
  1083. {
  1084. // LATER: Add vertical writing-mode support
  1085. var dir = this.textDirection;
  1086. if (dir == mxConstants.TEXT_DIRECTION_AUTO && this.dialect != mxConstants.DIALECT_STRICTHTML)
  1087. {
  1088. dir = this.getAutoDirection();
  1089. }
  1090. if (dir == mxConstants.TEXT_DIRECTION_LTR || dir == mxConstants.TEXT_DIRECTION_RTL)
  1091. {
  1092. divs[divs.length - 1].setAttribute('dir', dir);
  1093. }
  1094. else
  1095. {
  1096. divs[divs.length - 1].removeAttribute('dir');
  1097. }
  1098. }
  1099. }
  1100. };
  1101. /**
  1102. * Function: updateFont
  1103. *
  1104. * Updates the HTML node(s) to reflect the latest bounds and scale.
  1105. */
  1106. mxText.prototype.updateFont = function(node)
  1107. {
  1108. var style = node.style;
  1109. style.lineHeight = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? (this.size * mxConstants.LINE_HEIGHT) + 'px' : mxConstants.LINE_HEIGHT;
  1110. style.fontSize = this.size + 'px';
  1111. style.fontFamily = this.family;
  1112. style.verticalAlign = 'top';
  1113. style.color = this.color;
  1114. if ((this.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD)
  1115. {
  1116. style.fontWeight = 'bold';
  1117. }
  1118. else
  1119. {
  1120. style.fontWeight = '';
  1121. }
  1122. if ((this.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC)
  1123. {
  1124. style.fontStyle = 'italic';
  1125. }
  1126. else
  1127. {
  1128. style.fontStyle = '';
  1129. }
  1130. var txtDecor = [];
  1131. if ((this.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE)
  1132. {
  1133. txtDecor.push('underline');
  1134. }
  1135. if ((this.fontStyle & mxConstants.FONT_STRIKETHROUGH) == mxConstants.FONT_STRIKETHROUGH)
  1136. {
  1137. txtDecor.push('line-through');
  1138. }
  1139. style.textDecoration = txtDecor.join(' ');
  1140. if (this.align == mxConstants.ALIGN_CENTER)
  1141. {
  1142. style.textAlign = 'center';
  1143. }
  1144. else if (this.align == mxConstants.ALIGN_RIGHT)
  1145. {
  1146. style.textAlign = 'right';
  1147. }
  1148. else
  1149. {
  1150. style.textAlign = 'left';
  1151. }
  1152. };
  1153. /**
  1154. * Function: updateSize
  1155. *
  1156. * Updates the HTML node(s) to reflect the latest bounds and scale.
  1157. */
  1158. mxText.prototype.updateSize = function(node, enableWrap)
  1159. {
  1160. var w = Math.max(0, Math.round(this.bounds.width / this.scale));
  1161. var h = Math.max(0, Math.round(this.bounds.height / this.scale));
  1162. var style = node.style;
  1163. // NOTE: Do not use maxWidth here because wrapping will
  1164. // go wrong if the cell is outside of the viewable area
  1165. if (this.clipped)
  1166. {
  1167. style.overflow = 'hidden';
  1168. style.maxHeight = h + 'px';
  1169. style.maxWidth = w + 'px';
  1170. }
  1171. else if (this.overflow == 'fill')
  1172. {
  1173. style.width = (w + 1) + 'px';
  1174. style.height = (h + 1) + 'px';
  1175. style.overflow = 'hidden';
  1176. }
  1177. else if (this.overflow == 'width')
  1178. {
  1179. style.width = (w + 1) + 'px';
  1180. style.maxHeight = (h + 1) + 'px';
  1181. style.overflow = 'hidden';
  1182. }
  1183. else if (this.overflow == 'block')
  1184. {
  1185. style.width = (w + 1) + 'px';
  1186. }
  1187. if (this.wrap && w > 0)
  1188. {
  1189. style.wordWrap = mxConstants.WORD_WRAP;
  1190. style.whiteSpace = 'normal';
  1191. style.width = w + 'px';
  1192. if (enableWrap && this.overflow != 'fill' && this.overflow != 'width')
  1193. {
  1194. var sizeDiv = node;
  1195. if (sizeDiv.firstChild != null && sizeDiv.firstChild.nodeName == 'DIV')
  1196. {
  1197. sizeDiv = sizeDiv.firstChild;
  1198. if (node.style.wordWrap == 'break-word')
  1199. {
  1200. sizeDiv.style.width = '100%';
  1201. }
  1202. }
  1203. var tmp = sizeDiv.offsetWidth;
  1204. // Workaround for text measuring in hidden containers
  1205. if (tmp == 0)
  1206. {
  1207. var prev = node.parentNode;
  1208. node.style.visibility = 'hidden';
  1209. document.body.appendChild(node);
  1210. tmp = sizeDiv.offsetWidth;
  1211. node.style.visibility = '';
  1212. prev.appendChild(node);
  1213. }
  1214. tmp += 3;
  1215. if (this.clipped)
  1216. {
  1217. tmp = Math.min(tmp, w);
  1218. }
  1219. style.width = tmp + 'px';
  1220. }
  1221. }
  1222. else
  1223. {
  1224. style.whiteSpace = 'nowrap';
  1225. }
  1226. };
  1227. /**
  1228. * Function: updateMargin
  1229. *
  1230. * Updates the margin of this text shape.
  1231. */
  1232. mxText.prototype.updateMargin = function()
  1233. {
  1234. this.margin = mxUtils.getAlignmentAsPoint(this.align, this.valign);
  1235. };
  1236. /**
  1237. * Function: getSpacing
  1238. *
  1239. * Returns the spacing as an <mxPoint>.
  1240. */
  1241. mxText.prototype.getSpacing = function(noBase, margin)
  1242. {
  1243. var dx = 0;
  1244. var dy = 0;
  1245. if ((margin != null && margin.x == -0.5) ||
  1246. (margin == null && this.align == mxConstants.ALIGN_CENTER))
  1247. {
  1248. dx = (this.spacingLeft - this.spacingRight) / 2;
  1249. }
  1250. else if ((margin != null && margin.x == -1) ||
  1251. (margin == null && this.align == mxConstants.ALIGN_RIGHT))
  1252. {
  1253. dx = -this.spacingRight - (noBase? 0 : this.baseSpacingRight);
  1254. }
  1255. else
  1256. {
  1257. dx = this.spacingLeft + (noBase? 0 : this.baseSpacingLeft);
  1258. }
  1259. if (this.valign == mxConstants.ALIGN_MIDDLE)
  1260. {
  1261. dy = (this.spacingTop - this.spacingBottom) / 2;
  1262. }
  1263. else if (this.valign == mxConstants.ALIGN_BOTTOM)
  1264. {
  1265. dy = -this.spacingBottom - (noBase? 0 : this.baseSpacingBottom);
  1266. }
  1267. else
  1268. {
  1269. dy = this.spacingTop + (noBase? 0 : this.baseSpacingTop);
  1270. }
  1271. return new mxPoint(dx, dy);
  1272. };