mxText.js 36 KB

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