element.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950
  1. // Copyright 2007 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview A thicker wrapper around the DOM element returned from
  16. * the different draw methods of the graphics implementation, and
  17. * all interfaces that the various element types support.
  18. * @author robbyw@google.com (Robby Walker)
  19. */
  20. goog.provide('goog.graphics.ext.Element');
  21. goog.require('goog.events.EventTarget');
  22. goog.require('goog.functions');
  23. goog.require('goog.graphics.ext.coordinates');
  24. /**
  25. * Base class for a wrapper around the goog.graphics wrapper that enables
  26. * more advanced functionality.
  27. * @param {goog.graphics.ext.Group?} group Parent for this element.
  28. * @param {goog.graphics.Element} wrapper The thin wrapper to wrap.
  29. * @constructor
  30. * @extends {goog.events.EventTarget}
  31. */
  32. goog.graphics.ext.Element = function(group, wrapper) {
  33. goog.events.EventTarget.call(this);
  34. this.wrapper_ = wrapper;
  35. this.graphics_ = group ? group.getGraphics() : this;
  36. this.xPosition_ = new goog.graphics.ext.Element.Position_(this, true);
  37. this.yPosition_ = new goog.graphics.ext.Element.Position_(this, false);
  38. // Handle parent / child relationships.
  39. if (group) {
  40. this.parent_ = group;
  41. this.parent_.addChild(this);
  42. }
  43. };
  44. goog.inherits(goog.graphics.ext.Element, goog.events.EventTarget);
  45. /**
  46. * The graphics object that contains this element.
  47. * @type {goog.graphics.ext.Graphics|goog.graphics.ext.Element}
  48. * @private
  49. */
  50. goog.graphics.ext.Element.prototype.graphics_;
  51. /**
  52. * The goog.graphics wrapper this class wraps.
  53. * @type {goog.graphics.Element}
  54. * @private
  55. */
  56. goog.graphics.ext.Element.prototype.wrapper_;
  57. /**
  58. * The group or surface containing this element.
  59. * @type {goog.graphics.ext.Group|undefined}
  60. * @private
  61. */
  62. goog.graphics.ext.Element.prototype.parent_;
  63. /**
  64. * Whether or not computation of this element's position or size depends on its
  65. * parent's size.
  66. * @type {boolean}
  67. * @private
  68. */
  69. goog.graphics.ext.Element.prototype.parentDependent_ = false;
  70. /**
  71. * Whether the element has pending transformations.
  72. * @type {boolean}
  73. * @private
  74. */
  75. goog.graphics.ext.Element.prototype.needsTransform_ = false;
  76. /**
  77. * The current angle of rotation, expressed in degrees.
  78. * @type {number}
  79. * @private
  80. */
  81. goog.graphics.ext.Element.prototype.rotation_ = 0;
  82. /**
  83. * Object representing the x position and size of the element.
  84. * @type {goog.graphics.ext.Element.Position_}
  85. * @private
  86. */
  87. goog.graphics.ext.Element.prototype.xPosition_;
  88. /**
  89. * Object representing the y position and size of the element.
  90. * @type {goog.graphics.ext.Element.Position_}
  91. * @private
  92. */
  93. goog.graphics.ext.Element.prototype.yPosition_;
  94. /** @return {goog.graphics.Element} The underlying thin wrapper. */
  95. goog.graphics.ext.Element.prototype.getWrapper = function() {
  96. return this.wrapper_;
  97. };
  98. /**
  99. * @return {goog.graphics.ext.Element|goog.graphics.ext.Graphics} The graphics
  100. * surface the element is a part of.
  101. */
  102. goog.graphics.ext.Element.prototype.getGraphics = function() {
  103. return this.graphics_;
  104. };
  105. /**
  106. * Returns the graphics implementation.
  107. * @return {goog.graphics.AbstractGraphics} The underlying graphics
  108. * implementation drawing this element's wrapper.
  109. * @protected
  110. */
  111. goog.graphics.ext.Element.prototype.getGraphicsImplementation = function() {
  112. return this.graphics_.getImplementation();
  113. };
  114. /**
  115. * @return {goog.graphics.ext.Group|undefined} The parent of this element.
  116. */
  117. goog.graphics.ext.Element.prototype.getParent = function() {
  118. return this.parent_;
  119. };
  120. // GENERAL POSITIONING
  121. /**
  122. * Internal convenience method for setting position - either as a left/top,
  123. * center/middle, or right/bottom value. Only one should be specified.
  124. * @param {goog.graphics.ext.Element.Position_} position The position object to
  125. * set the value on.
  126. * @param {number|string} value The value of the coordinate.
  127. * @param {goog.graphics.ext.Element.PositionType_} type The type of the
  128. * coordinate.
  129. * @param {boolean=} opt_chain Optional flag to specify this function is part
  130. * of a chain of calls and therefore transformations should be set as
  131. * pending but not yet performed.
  132. * @private
  133. */
  134. goog.graphics.ext.Element.prototype.setPosition_ = function(
  135. position, value, type, opt_chain) {
  136. position.setPosition(value, type);
  137. this.computeIsParentDependent_(position);
  138. this.needsTransform_ = true;
  139. if (!opt_chain) {
  140. this.transform();
  141. }
  142. };
  143. /**
  144. * Sets the width/height of the element.
  145. * @param {goog.graphics.ext.Element.Position_} position The position object to
  146. * set the value on.
  147. * @param {string|number} size The new width/height value.
  148. * @param {boolean=} opt_chain Optional flag to specify this function is part
  149. * of a chain of calls and therefore transformations should be set as
  150. * pending but not yet performed.
  151. * @private
  152. */
  153. goog.graphics.ext.Element.prototype.setSize_ = function(
  154. position, size, opt_chain) {
  155. if (position.setSize(size)) {
  156. this.needsTransform_ = true;
  157. this.computeIsParentDependent_(position);
  158. if (!opt_chain) {
  159. this.reset();
  160. }
  161. } else if (!opt_chain && this.isPendingTransform()) {
  162. this.reset();
  163. }
  164. };
  165. /**
  166. * Sets the minimum width/height of the element.
  167. * @param {goog.graphics.ext.Element.Position_} position The position object to
  168. * set the value on.
  169. * @param {string|number} minSize The minimum width/height of the element.
  170. * @private
  171. */
  172. goog.graphics.ext.Element.prototype.setMinSize_ = function(position, minSize) {
  173. position.setMinSize(minSize);
  174. this.needsTransform_ = true;
  175. this.computeIsParentDependent_(position);
  176. };
  177. // HORIZONTAL POSITIONING
  178. /**
  179. * @return {number} The distance from the left edge of this element to the left
  180. * edge of its parent, specified in units of the parent's coordinate system.
  181. */
  182. goog.graphics.ext.Element.prototype.getLeft = function() {
  183. return this.xPosition_.getStart();
  184. };
  185. /**
  186. * Sets the left coordinate of the element. Overwrites any previous value of
  187. * left, center, or right for this element.
  188. * @param {string|number} left The left coordinate.
  189. * @param {boolean=} opt_chain Optional flag to specify this function is part
  190. * of a chain of calls and therefore transformations should be set as
  191. * pending but not yet performed.
  192. */
  193. goog.graphics.ext.Element.prototype.setLeft = function(left, opt_chain) {
  194. this.setPosition_(
  195. this.xPosition_, left, goog.graphics.ext.Element.PositionType_.START,
  196. opt_chain);
  197. };
  198. /**
  199. * @return {number} The right coordinate of the element, in units of the
  200. * parent's coordinate system.
  201. */
  202. goog.graphics.ext.Element.prototype.getRight = function() {
  203. return this.xPosition_.getEnd();
  204. };
  205. /**
  206. * Sets the right coordinate of the element. Overwrites any previous value of
  207. * left, center, or right for this element.
  208. * @param {string|number} right The right coordinate.
  209. * @param {boolean=} opt_chain Optional flag to specify this function is part
  210. * of a chain of calls and therefore transformations should be set as
  211. * pending but not yet performed.
  212. */
  213. goog.graphics.ext.Element.prototype.setRight = function(right, opt_chain) {
  214. this.setPosition_(
  215. this.xPosition_, right, goog.graphics.ext.Element.PositionType_.END,
  216. opt_chain);
  217. };
  218. /**
  219. * @return {number} The center coordinate of the element, in units of the
  220. * parent's coordinate system.
  221. */
  222. goog.graphics.ext.Element.prototype.getCenter = function() {
  223. return this.xPosition_.getMiddle();
  224. };
  225. /**
  226. * Sets the center coordinate of the element. Overwrites any previous value of
  227. * left, center, or right for this element.
  228. * @param {string|number} center The center coordinate.
  229. * @param {boolean=} opt_chain Optional flag to specify this function is part
  230. * of a chain of calls and therefore transformations should be set as
  231. * pending but not yet performed.
  232. */
  233. goog.graphics.ext.Element.prototype.setCenter = function(center, opt_chain) {
  234. this.setPosition_(
  235. this.xPosition_, center, goog.graphics.ext.Element.PositionType_.MIDDLE,
  236. opt_chain);
  237. };
  238. // VERTICAL POSITIONING
  239. /**
  240. * @return {number} The distance from the top edge of this element to the top
  241. * edge of its parent, specified in units of the parent's coordinate system.
  242. */
  243. goog.graphics.ext.Element.prototype.getTop = function() {
  244. return this.yPosition_.getStart();
  245. };
  246. /**
  247. * Sets the top coordinate of the element. Overwrites any previous value of
  248. * top, middle, or bottom for this element.
  249. * @param {string|number} top The top coordinate.
  250. * @param {boolean=} opt_chain Optional flag to specify this function is part
  251. * of a chain of calls and therefore transformations should be set as
  252. * pending but not yet performed.
  253. */
  254. goog.graphics.ext.Element.prototype.setTop = function(top, opt_chain) {
  255. this.setPosition_(
  256. this.yPosition_, top, goog.graphics.ext.Element.PositionType_.START,
  257. opt_chain);
  258. };
  259. /**
  260. * @return {number} The bottom coordinate of the element, in units of the
  261. * parent's coordinate system.
  262. */
  263. goog.graphics.ext.Element.prototype.getBottom = function() {
  264. return this.yPosition_.getEnd();
  265. };
  266. /**
  267. * Sets the bottom coordinate of the element. Overwrites any previous value of
  268. * top, middle, or bottom for this element.
  269. * @param {string|number} bottom The bottom coordinate.
  270. * @param {boolean=} opt_chain Optional flag to specify this function is part
  271. * of a chain of calls and therefore transformations should be set as
  272. * pending but not yet performed.
  273. */
  274. goog.graphics.ext.Element.prototype.setBottom = function(bottom, opt_chain) {
  275. this.setPosition_(
  276. this.yPosition_, bottom, goog.graphics.ext.Element.PositionType_.END,
  277. opt_chain);
  278. };
  279. /**
  280. * @return {number} The middle coordinate of the element, in units of the
  281. * parent's coordinate system.
  282. */
  283. goog.graphics.ext.Element.prototype.getMiddle = function() {
  284. return this.yPosition_.getMiddle();
  285. };
  286. /**
  287. * Sets the middle coordinate of the element. Overwrites any previous value of
  288. * top, middle, or bottom for this element
  289. * @param {string|number} middle The middle coordinate.
  290. * @param {boolean=} opt_chain Optional flag to specify this function is part
  291. * of a chain of calls and therefore transformations should be set as
  292. * pending but not yet performed.
  293. */
  294. goog.graphics.ext.Element.prototype.setMiddle = function(middle, opt_chain) {
  295. this.setPosition_(
  296. this.yPosition_, middle, goog.graphics.ext.Element.PositionType_.MIDDLE,
  297. opt_chain);
  298. };
  299. // DIMENSIONS
  300. /**
  301. * @return {number} The width of the element, in units of the parent's
  302. * coordinate system.
  303. */
  304. goog.graphics.ext.Element.prototype.getWidth = function() {
  305. return this.xPosition_.getSize();
  306. };
  307. /**
  308. * Sets the width of the element.
  309. * @param {string|number} width The new width value.
  310. * @param {boolean=} opt_chain Optional flag to specify this function is part
  311. * of a chain of calls and therefore transformations should be set as
  312. * pending but not yet performed.
  313. */
  314. goog.graphics.ext.Element.prototype.setWidth = function(width, opt_chain) {
  315. this.setSize_(this.xPosition_, width, opt_chain);
  316. };
  317. /**
  318. * @return {number} The minimum width of the element, in units of the parent's
  319. * coordinate system.
  320. */
  321. goog.graphics.ext.Element.prototype.getMinWidth = function() {
  322. return this.xPosition_.getMinSize();
  323. };
  324. /**
  325. * Sets the minimum width of the element.
  326. * @param {string|number} minWidth The minimum width of the element.
  327. */
  328. goog.graphics.ext.Element.prototype.setMinWidth = function(minWidth) {
  329. this.setMinSize_(this.xPosition_, minWidth);
  330. };
  331. /**
  332. * @return {number} The height of the element, in units of the parent's
  333. * coordinate system.
  334. */
  335. goog.graphics.ext.Element.prototype.getHeight = function() {
  336. return this.yPosition_.getSize();
  337. };
  338. /**
  339. * Sets the height of the element.
  340. * @param {string|number} height The new height value.
  341. * @param {boolean=} opt_chain Optional flag to specify this function is part
  342. * of a chain of calls and therefore transformations should be set as
  343. * pending but not yet performed.
  344. */
  345. goog.graphics.ext.Element.prototype.setHeight = function(height, opt_chain) {
  346. this.setSize_(this.yPosition_, height, opt_chain);
  347. };
  348. /**
  349. * @return {number} The minimum height of the element, in units of the parent's
  350. * coordinate system.
  351. */
  352. goog.graphics.ext.Element.prototype.getMinHeight = function() {
  353. return this.yPosition_.getMinSize();
  354. };
  355. /**
  356. * Sets the minimum height of the element.
  357. * @param {string|number} minHeight The minimum height of the element.
  358. */
  359. goog.graphics.ext.Element.prototype.setMinHeight = function(minHeight) {
  360. this.setMinSize_(this.yPosition_, minHeight);
  361. };
  362. // BOUNDS SHORTCUTS
  363. /**
  364. * Shortcut for setting the left and top position.
  365. * @param {string|number} left The left coordinate.
  366. * @param {string|number} top The top coordinate.
  367. * @param {boolean=} opt_chain Optional flag to specify this function is part
  368. * of a chain of calls and therefore transformations should be set as
  369. * pending but not yet performed.
  370. */
  371. goog.graphics.ext.Element.prototype.setPosition = function(
  372. left, top, opt_chain) {
  373. this.setLeft(left, true);
  374. this.setTop(top, opt_chain);
  375. };
  376. /**
  377. * Shortcut for setting the width and height.
  378. * @param {string|number} width The new width value.
  379. * @param {string|number} height The new height value.
  380. * @param {boolean=} opt_chain Optional flag to specify this function is part
  381. * of a chain of calls and therefore transformations should be set as
  382. * pending but not yet performed.
  383. */
  384. goog.graphics.ext.Element.prototype.setSize = function(
  385. width, height, opt_chain) {
  386. this.setWidth(width, true);
  387. this.setHeight(height, opt_chain);
  388. };
  389. /**
  390. * Shortcut for setting the left, top, width, and height.
  391. * @param {string|number} left The left coordinate.
  392. * @param {string|number} top The top coordinate.
  393. * @param {string|number} width The new width value.
  394. * @param {string|number} height The new height value.
  395. * @param {boolean=} opt_chain Optional flag to specify this function is part
  396. * of a chain of calls and therefore transformations should be set as
  397. * pending but not yet performed.
  398. */
  399. goog.graphics.ext.Element.prototype.setBounds = function(
  400. left, top, width, height, opt_chain) {
  401. this.setLeft(left, true);
  402. this.setTop(top, true);
  403. this.setWidth(width, true);
  404. this.setHeight(height, opt_chain);
  405. };
  406. // MAXIMUM BOUNDS
  407. /**
  408. * @return {number} An estimate of the maximum x extent this element would have
  409. * in a parent of no width.
  410. */
  411. goog.graphics.ext.Element.prototype.getMaxX = function() {
  412. return this.xPosition_.getMaxPosition();
  413. };
  414. /**
  415. * @return {number} An estimate of the maximum y extent this element would have
  416. * in a parent of no height.
  417. */
  418. goog.graphics.ext.Element.prototype.getMaxY = function() {
  419. return this.yPosition_.getMaxPosition();
  420. };
  421. // RESET
  422. /**
  423. * Reset the element. This is called when the element changes size, or when
  424. * the coordinate system changes in a way that would affect pixel based
  425. * rendering
  426. */
  427. goog.graphics.ext.Element.prototype.reset = function() {
  428. this.xPosition_.resetCache();
  429. this.yPosition_.resetCache();
  430. this.redraw();
  431. this.needsTransform_ = true;
  432. this.transform();
  433. };
  434. /**
  435. * Overridable function for subclass specific reset.
  436. * @protected
  437. */
  438. goog.graphics.ext.Element.prototype.redraw = goog.nullFunction;
  439. // PARENT DEPENDENCY
  440. /**
  441. * Computes whether the element is still parent dependent.
  442. * @param {goog.graphics.ext.Element.Position_} position The recently changed
  443. * position object.
  444. * @private
  445. */
  446. goog.graphics.ext.Element.prototype.computeIsParentDependent_ = function(
  447. position) {
  448. this.parentDependent_ = position.isParentDependent() ||
  449. this.xPosition_.isParentDependent() ||
  450. this.yPosition_.isParentDependent() || this.checkParentDependent();
  451. };
  452. /**
  453. * Returns whether this element's bounds depend on its parents.
  454. *
  455. * This function should be treated as if it has package scope.
  456. * @return {boolean} Whether this element's bounds depend on its parents.
  457. */
  458. goog.graphics.ext.Element.prototype.isParentDependent = function() {
  459. return this.parentDependent_;
  460. };
  461. /**
  462. * Overridable function for subclass specific parent dependency.
  463. * @return {boolean} Whether this shape's bounds depends on its parent's.
  464. * @protected
  465. */
  466. goog.graphics.ext.Element.prototype.checkParentDependent = goog.functions.FALSE;
  467. // ROTATION
  468. /**
  469. * Set the rotation of this element.
  470. * @param {number} angle The angle of rotation, in degrees.
  471. */
  472. goog.graphics.ext.Element.prototype.setRotation = function(angle) {
  473. if (this.rotation_ != angle) {
  474. this.rotation_ = angle;
  475. this.needsTransform_ = true;
  476. this.transform();
  477. }
  478. };
  479. /**
  480. * @return {number} The angle of rotation of this element, in degrees.
  481. */
  482. goog.graphics.ext.Element.prototype.getRotation = function() {
  483. return this.rotation_;
  484. };
  485. // TRANSFORMS
  486. /**
  487. * Called by the parent when the parent has transformed.
  488. *
  489. * Should be treated as package scope.
  490. */
  491. goog.graphics.ext.Element.prototype.parentTransform = function() {
  492. this.needsTransform_ = this.needsTransform_ || this.parentDependent_;
  493. };
  494. /**
  495. * @return {boolean} Whether this element has pending transforms.
  496. */
  497. goog.graphics.ext.Element.prototype.isPendingTransform = function() {
  498. return this.needsTransform_;
  499. };
  500. /**
  501. * Performs a pending transform.
  502. * @protected
  503. */
  504. goog.graphics.ext.Element.prototype.transform = function() {
  505. if (this.isPendingTransform()) {
  506. this.needsTransform_ = false;
  507. this.wrapper_.setTransformation(
  508. this.getLeft(), this.getTop(), this.rotation_,
  509. (this.getWidth() || 1) / 2, (this.getHeight() || 1) / 2);
  510. // TODO(robbyw): this._fireEvent('transform', [ this ]);
  511. }
  512. };
  513. // PIXEL SCALE
  514. /**
  515. * @return {number} Returns the number of pixels per unit in the x direction.
  516. */
  517. goog.graphics.ext.Element.prototype.getPixelScaleX = function() {
  518. return this.getGraphics().getPixelScaleX();
  519. };
  520. /**
  521. * @return {number} Returns the number of pixels per unit in the y direction.
  522. */
  523. goog.graphics.ext.Element.prototype.getPixelScaleY = function() {
  524. return this.getGraphics().getPixelScaleY();
  525. };
  526. // EVENT HANDLING
  527. /** @override */
  528. goog.graphics.ext.Element.prototype.disposeInternal = function() {
  529. goog.graphics.ext.Element.superClass_.disposeInternal.call(this);
  530. this.wrapper_.dispose();
  531. };
  532. // INTERNAL POSITION OBJECT
  533. /**
  534. * Position specification types. Start corresponds to left/top, middle to
  535. * center/middle, and end to right/bottom.
  536. * @enum {number}
  537. * @private
  538. */
  539. goog.graphics.ext.Element.PositionType_ = {
  540. START: 0,
  541. MIDDLE: 1,
  542. END: 2
  543. };
  544. /**
  545. * Manages a position and size, either horizontal or vertical.
  546. * @param {goog.graphics.ext.Element} element The element the position applies
  547. * to.
  548. * @param {boolean} horizontal Whether the position is horizontal or vertical.
  549. * @constructor
  550. * @private
  551. */
  552. goog.graphics.ext.Element.Position_ = function(element, horizontal) {
  553. this.element_ = element;
  554. this.horizontal_ = horizontal;
  555. };
  556. /**
  557. * @return {!Object} The coordinate value computation cache.
  558. * @private
  559. */
  560. goog.graphics.ext.Element.Position_.prototype.getCoordinateCache_ = function() {
  561. return this.coordinateCache_ || (this.coordinateCache_ = {});
  562. };
  563. /**
  564. * @return {number} The size of the parent's coordinate space.
  565. * @private
  566. */
  567. goog.graphics.ext.Element.Position_.prototype.getParentSize_ = function() {
  568. var parent = this.element_.getParent();
  569. return this.horizontal_ ? parent.getCoordinateWidth() :
  570. parent.getCoordinateHeight();
  571. };
  572. /**
  573. * @return {number} The minimum width/height of the element.
  574. */
  575. goog.graphics.ext.Element.Position_.prototype.getMinSize = function() {
  576. return this.getValue_(this.minSize_);
  577. };
  578. /**
  579. * Sets the minimum width/height of the element.
  580. * @param {string|number} minSize The minimum width/height of the element.
  581. */
  582. goog.graphics.ext.Element.Position_.prototype.setMinSize = function(minSize) {
  583. this.minSize_ = minSize;
  584. this.resetCache();
  585. };
  586. /**
  587. * @return {number} The width/height of the element.
  588. */
  589. goog.graphics.ext.Element.Position_.prototype.getSize = function() {
  590. return Math.max(this.getValue_(this.size_), this.getMinSize());
  591. };
  592. /**
  593. * Sets the width/height of the element.
  594. * @param {string|number} size The width/height of the element.
  595. * @return {boolean} Whether the value was changed.
  596. */
  597. goog.graphics.ext.Element.Position_.prototype.setSize = function(size) {
  598. if (size != this.size_) {
  599. this.size_ = size;
  600. this.resetCache();
  601. return true;
  602. }
  603. return false;
  604. };
  605. /**
  606. * Converts the given x coordinate to a number value in units.
  607. * @param {string|number} v The coordinate to retrieve the value for.
  608. * @param {boolean=} opt_forMaximum Whether we are computing the largest value
  609. * this coordinate would be in a parent of no size.
  610. * @return {number} The correct number of coordinate space units.
  611. * @private
  612. */
  613. goog.graphics.ext.Element.Position_.prototype.getValue_ = function(
  614. v, opt_forMaximum) {
  615. if (!goog.graphics.ext.coordinates.isSpecial(v)) {
  616. return parseFloat(String(v));
  617. }
  618. var cache = this.getCoordinateCache_();
  619. var scale = this.horizontal_ ? this.element_.getPixelScaleX() :
  620. this.element_.getPixelScaleY();
  621. var containerSize;
  622. if (opt_forMaximum) {
  623. containerSize =
  624. goog.graphics.ext.coordinates.computeValue(this.size_ || 0, 0, scale);
  625. } else {
  626. var parent = this.element_.getParent();
  627. containerSize = this.horizontal_ ? parent.getWidth() : parent.getHeight();
  628. }
  629. return goog.graphics.ext.coordinates.getValue(
  630. v, opt_forMaximum, containerSize, scale, cache);
  631. };
  632. /**
  633. * @return {number} The distance from the left/top edge of this element to the
  634. * left/top edge of its parent, specified in units of the parent's
  635. * coordinate system.
  636. */
  637. goog.graphics.ext.Element.Position_.prototype.getStart = function() {
  638. if (this.cachedValue_ == null) {
  639. var value = this.getValue_(this.distance_);
  640. if (this.distanceType_ == goog.graphics.ext.Element.PositionType_.START) {
  641. this.cachedValue_ = value;
  642. } else if (
  643. this.distanceType_ == goog.graphics.ext.Element.PositionType_.MIDDLE) {
  644. this.cachedValue_ = value + (this.getParentSize_() - this.getSize()) / 2;
  645. } else {
  646. this.cachedValue_ = this.getParentSize_() - value - this.getSize();
  647. }
  648. }
  649. return this.cachedValue_;
  650. };
  651. /**
  652. * @return {number} The middle coordinate of the element, in units of the
  653. * parent's coordinate system.
  654. */
  655. goog.graphics.ext.Element.Position_.prototype.getMiddle = function() {
  656. return this.distanceType_ == goog.graphics.ext.Element.PositionType_.MIDDLE ?
  657. this.getValue_(this.distance_) :
  658. (this.getParentSize_() - this.getSize()) / 2 - this.getStart();
  659. };
  660. /**
  661. * @return {number} The end coordinate of the element, in units of the
  662. * parent's coordinate system.
  663. */
  664. goog.graphics.ext.Element.Position_.prototype.getEnd = function() {
  665. return this.distanceType_ == goog.graphics.ext.Element.PositionType_.END ?
  666. this.getValue_(this.distance_) :
  667. this.getParentSize_() - this.getStart() - this.getSize();
  668. };
  669. /**
  670. * Sets the position, either as a left/top, center/middle, or right/bottom
  671. * value.
  672. * @param {number|string} value The value of the coordinate.
  673. * @param {goog.graphics.ext.Element.PositionType_} type The type of the
  674. * coordinate.
  675. */
  676. goog.graphics.ext.Element.Position_.prototype.setPosition = function(
  677. value, type) {
  678. this.distance_ = value;
  679. this.distanceType_ = type;
  680. // Clear cached value.
  681. this.cachedValue_ = null;
  682. };
  683. /**
  684. * @return {number} An estimate of the maximum x/y extent this element would
  685. * have in a parent of no width/height.
  686. */
  687. goog.graphics.ext.Element.Position_.prototype.getMaxPosition = function() {
  688. // TODO(robbyw): Handle transformed or rotated coordinates
  689. // TODO(robbyw): Handle pixel based sizes?
  690. return this.getValue_(this.distance_ || 0) +
  691. (goog.graphics.ext.coordinates.isSpecial(this.size_) ? 0 :
  692. this.getSize());
  693. };
  694. /**
  695. * Resets the caches of position values and coordinate values.
  696. */
  697. goog.graphics.ext.Element.Position_.prototype.resetCache = function() {
  698. this.coordinateCache_ = null;
  699. this.cachedValue_ = null;
  700. };
  701. /**
  702. * @return {boolean} Whether the size or position of this element depends on
  703. * the size of the parent element.
  704. */
  705. goog.graphics.ext.Element.Position_.prototype.isParentDependent = function() {
  706. return this.distanceType_ != goog.graphics.ext.Element.PositionType_.START ||
  707. goog.graphics.ext.coordinates.isSpecial(this.size_) ||
  708. goog.graphics.ext.coordinates.isSpecial(this.minSize_) ||
  709. goog.graphics.ext.coordinates.isSpecial(this.distance_);
  710. };
  711. /**
  712. * The lazy loaded distance from the parent's top/left edge to this element's
  713. * top/left edge expressed in the parent's coordinate system. We cache this
  714. * because it is most freqeuently requested by the element and it is easy to
  715. * compute middle and end values from it.
  716. * @type {?number}
  717. * @private
  718. */
  719. goog.graphics.ext.Element.Position_.prototype.cachedValue_ = null;
  720. /**
  721. * A cache of computed x coordinates.
  722. * @type {Object}
  723. * @private
  724. */
  725. goog.graphics.ext.Element.Position_.prototype.coordinateCache_ = null;
  726. /**
  727. * The minimum width/height of this element, as specified by the caller.
  728. * @type {string|number}
  729. * @private
  730. */
  731. goog.graphics.ext.Element.Position_.prototype.minSize_ = 0;
  732. /**
  733. * The width/height of this object, as specified by the caller.
  734. * @type {string|number}
  735. * @private
  736. */
  737. goog.graphics.ext.Element.Position_.prototype.size_ = 0;
  738. /**
  739. * The coordinate of this object, as specified by the caller. The type of
  740. * coordinate is specified by distanceType_.
  741. * @type {string|number}
  742. * @private
  743. */
  744. goog.graphics.ext.Element.Position_.prototype.distance_ = 0;
  745. /**
  746. * The coordinate type specified by distance_.
  747. * @type {goog.graphics.ext.Element.PositionType_}
  748. * @private
  749. */
  750. goog.graphics.ext.Element.Position_.prototype.distanceType_ =
  751. goog.graphics.ext.Element.PositionType_.START;