basenode.js 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526
  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 Definition of the goog.ui.tree.BaseNode class.
  16. *
  17. * @author arv@google.com (Erik Arvidsson)
  18. * @author eae@google.com (Emil A Eklund)
  19. *
  20. * This is a based on the webfx tree control. It since been updated to add
  21. * typeahead support, as well as accessibility support using ARIA framework.
  22. * See file comment in treecontrol.js.
  23. */
  24. goog.provide('goog.ui.tree.BaseNode');
  25. goog.provide('goog.ui.tree.BaseNode.EventType');
  26. goog.require('goog.Timer');
  27. goog.require('goog.a11y.aria');
  28. goog.require('goog.a11y.aria.State');
  29. goog.require('goog.asserts');
  30. goog.require('goog.dom.safe');
  31. goog.require('goog.events.Event');
  32. goog.require('goog.events.KeyCodes');
  33. goog.require('goog.html.SafeHtml');
  34. goog.require('goog.html.SafeStyle');
  35. goog.require('goog.string');
  36. goog.require('goog.string.StringBuffer');
  37. goog.require('goog.style');
  38. goog.require('goog.ui.Component');
  39. goog.forwardDeclare('goog.ui.tree.TreeControl'); // circular
  40. /**
  41. * An abstract base class for a node in the tree.
  42. *
  43. * @param {string|!goog.html.SafeHtml} content The content of the node label.
  44. * Strings are treated as plain-text and will be HTML escaped.
  45. * @param {Object=} opt_config The configuration for the tree. See
  46. * {@link goog.ui.tree.BaseNode.defaultConfig}. If not specified the
  47. * default config will be used.
  48. * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
  49. * @constructor
  50. * @extends {goog.ui.Component}
  51. */
  52. goog.ui.tree.BaseNode = function(content, opt_config, opt_domHelper) {
  53. goog.ui.Component.call(this, opt_domHelper);
  54. /**
  55. * The configuration for the tree.
  56. * @type {Object}
  57. * @private
  58. */
  59. this.config_ = opt_config || goog.ui.tree.BaseNode.defaultConfig;
  60. /**
  61. * HTML content of the node label.
  62. * @type {!goog.html.SafeHtml}
  63. * @private
  64. */
  65. this.html_ = goog.html.SafeHtml.htmlEscapePreservingNewlines(content);
  66. /** @private {string} */
  67. this.iconClass_;
  68. /** @private {string} */
  69. this.expandedIconClass_;
  70. /** @protected {goog.ui.tree.TreeControl} */
  71. this.tree;
  72. /** @private {goog.ui.tree.BaseNode} */
  73. this.previousSibling_;
  74. /** @private {goog.ui.tree.BaseNode} */
  75. this.nextSibling_;
  76. /** @private {goog.ui.tree.BaseNode} */
  77. this.firstChild_;
  78. /** @private {goog.ui.tree.BaseNode} */
  79. this.lastChild_;
  80. /**
  81. * Whether the tree item is selected.
  82. * @private {boolean}
  83. */
  84. this.selected_ = false;
  85. /**
  86. * Whether the tree node is expanded.
  87. * @private {boolean}
  88. */
  89. this.expanded_ = false;
  90. /**
  91. * Tooltip for the tree item
  92. * @private {?string}
  93. */
  94. this.toolTip_ = null;
  95. /**
  96. * HTML that can appear after the label (so not inside the anchor).
  97. * @private {!goog.html.SafeHtml}
  98. */
  99. this.afterLabelHtml_ = goog.html.SafeHtml.EMPTY;
  100. /**
  101. * Whether to allow user to collapse this node.
  102. * @private {boolean}
  103. */
  104. this.isUserCollapsible_ = true;
  105. /**
  106. * Nesting depth of this node; cached result of computeDepth_.
  107. * -1 if value has not been cached.
  108. * @private {number}
  109. */
  110. this.depth_ = -1;
  111. };
  112. goog.inherits(goog.ui.tree.BaseNode, goog.ui.Component);
  113. /**
  114. * The event types dispatched by this class.
  115. * @enum {string}
  116. */
  117. goog.ui.tree.BaseNode.EventType = {
  118. BEFORE_EXPAND: 'beforeexpand',
  119. EXPAND: 'expand',
  120. BEFORE_COLLAPSE: 'beforecollapse',
  121. COLLAPSE: 'collapse'
  122. };
  123. /**
  124. * Map of nodes in existence. Needed to route events to the appropriate nodes.
  125. * Nodes are added to the map at {@link #enterDocument} time and removed at
  126. * {@link #exitDocument} time.
  127. * @type {Object}
  128. * @protected
  129. */
  130. goog.ui.tree.BaseNode.allNodes = {};
  131. /** @override */
  132. goog.ui.tree.BaseNode.prototype.disposeInternal = function() {
  133. goog.ui.tree.BaseNode.superClass_.disposeInternal.call(this);
  134. if (this.tree) {
  135. this.tree.removeNode(this);
  136. this.tree = null;
  137. }
  138. this.setElementInternal(null);
  139. };
  140. /**
  141. * Adds roles and states.
  142. * @protected
  143. */
  144. goog.ui.tree.BaseNode.prototype.initAccessibility = function() {
  145. var el = this.getElement();
  146. if (el) {
  147. // Set an id for the label
  148. var label = this.getLabelElement();
  149. if (label && !label.id) {
  150. label.id = this.getId() + '.label';
  151. }
  152. goog.a11y.aria.setRole(el, 'treeitem');
  153. goog.a11y.aria.setState(el, 'selected', false);
  154. goog.a11y.aria.setState(el, 'level', this.getDepth());
  155. if (label) {
  156. goog.a11y.aria.setState(el, 'labelledby', label.id);
  157. }
  158. var img = this.getIconElement();
  159. if (img) {
  160. goog.a11y.aria.setRole(img, 'presentation');
  161. }
  162. var ei = this.getExpandIconElement();
  163. if (ei) {
  164. goog.a11y.aria.setRole(ei, 'presentation');
  165. }
  166. var ce = this.getChildrenElement();
  167. if (ce) {
  168. goog.a11y.aria.setRole(ce, 'group');
  169. // In case the children will be created lazily.
  170. if (ce.hasChildNodes()) {
  171. // Only set aria-expanded if the node has children (can be expanded).
  172. goog.a11y.aria.setState(el, goog.a11y.aria.State.EXPANDED, false);
  173. // do setsize for each child
  174. var count = this.getChildCount();
  175. for (var i = 1; i <= count; i++) {
  176. var child = this.getChildAt(i - 1).getElement();
  177. goog.asserts.assert(child, 'The child element cannot be null');
  178. goog.a11y.aria.setState(child, 'setsize', count);
  179. goog.a11y.aria.setState(child, 'posinset', i);
  180. }
  181. }
  182. }
  183. }
  184. };
  185. /** @override */
  186. goog.ui.tree.BaseNode.prototype.createDom = function() {
  187. var element = this.getDomHelper().safeHtmlToNode(this.toSafeHtml());
  188. this.setElementInternal(/** @type {!Element} */ (element));
  189. };
  190. /** @override */
  191. goog.ui.tree.BaseNode.prototype.enterDocument = function() {
  192. goog.ui.tree.BaseNode.superClass_.enterDocument.call(this);
  193. goog.ui.tree.BaseNode.allNodes[this.getId()] = this;
  194. this.initAccessibility();
  195. };
  196. /** @override */
  197. goog.ui.tree.BaseNode.prototype.exitDocument = function() {
  198. goog.ui.tree.BaseNode.superClass_.exitDocument.call(this);
  199. delete goog.ui.tree.BaseNode.allNodes[this.getId()];
  200. };
  201. /**
  202. * The method assumes that the child doesn't have parent node yet.
  203. * The {@code opt_render} argument is not used. If the parent node is expanded,
  204. * the child node's state will be the same as the parent's. Otherwise the
  205. * child's DOM tree won't be created.
  206. * @override
  207. */
  208. goog.ui.tree.BaseNode.prototype.addChildAt = function(
  209. child, index, opt_render) {
  210. goog.asserts.assert(!child.getParent());
  211. goog.asserts.assertInstanceof(child, goog.ui.tree.BaseNode);
  212. var prevNode = this.getChildAt(index - 1);
  213. var nextNode = this.getChildAt(index);
  214. goog.ui.tree.BaseNode.superClass_.addChildAt.call(this, child, index);
  215. child.previousSibling_ = prevNode;
  216. child.nextSibling_ = nextNode;
  217. if (prevNode) {
  218. prevNode.nextSibling_ = child;
  219. } else {
  220. this.firstChild_ = child;
  221. }
  222. if (nextNode) {
  223. nextNode.previousSibling_ = child;
  224. } else {
  225. this.lastChild_ = child;
  226. }
  227. var tree = this.getTree();
  228. if (tree) {
  229. child.setTreeInternal(tree);
  230. }
  231. child.setDepth_(this.getDepth() + 1);
  232. var el = this.getElement();
  233. if (el) {
  234. this.updateExpandIcon();
  235. goog.a11y.aria.setState(
  236. el, goog.a11y.aria.State.EXPANDED, this.getExpanded());
  237. if (this.getExpanded()) {
  238. var childrenEl = this.getChildrenElement();
  239. if (!child.getElement()) {
  240. child.createDom();
  241. }
  242. var childElement = child.getElement();
  243. var nextElement = nextNode && nextNode.getElement();
  244. childrenEl.insertBefore(childElement, nextElement);
  245. if (this.isInDocument()) {
  246. child.enterDocument();
  247. }
  248. if (!nextNode) {
  249. if (prevNode) {
  250. prevNode.updateExpandIcon();
  251. } else {
  252. goog.style.setElementShown(childrenEl, true);
  253. this.setExpanded(this.getExpanded());
  254. }
  255. }
  256. }
  257. }
  258. };
  259. /**
  260. * Adds a node as a child to the current node.
  261. * @param {goog.ui.tree.BaseNode} child The child to add.
  262. * @param {goog.ui.tree.BaseNode=} opt_before If specified, the new child is
  263. * added as a child before this one. If not specified, it's appended to the
  264. * end.
  265. * @return {!goog.ui.tree.BaseNode} The added child.
  266. */
  267. goog.ui.tree.BaseNode.prototype.add = function(child, opt_before) {
  268. goog.asserts.assert(
  269. !opt_before || opt_before.getParent() == this,
  270. 'Can only add nodes before siblings');
  271. if (child.getParent()) {
  272. child.getParent().removeChild(child);
  273. }
  274. this.addChildAt(
  275. child, opt_before ? this.indexOfChild(opt_before) : this.getChildCount());
  276. return child;
  277. };
  278. /**
  279. * Removes a child. The caller is responsible for disposing the node.
  280. * @param {goog.ui.Component|string} childNode The child to remove. Must be a
  281. * {@link goog.ui.tree.BaseNode}.
  282. * @param {boolean=} opt_unrender Unused. The child will always be unrendered.
  283. * @return {!goog.ui.tree.BaseNode} The child that was removed.
  284. * @override
  285. */
  286. goog.ui.tree.BaseNode.prototype.removeChild = function(
  287. childNode, opt_unrender) {
  288. // In reality, this only accepts BaseNodes.
  289. var child = /** @type {goog.ui.tree.BaseNode} */ (childNode);
  290. // if we remove selected or tree with the selected we should select this
  291. var tree = this.getTree();
  292. var selectedNode = tree ? tree.getSelectedItem() : null;
  293. if (selectedNode == child || child.contains(selectedNode)) {
  294. if (tree.hasFocus()) {
  295. this.select();
  296. goog.Timer.callOnce(this.onTimeoutSelect_, 10, this);
  297. } else {
  298. this.select();
  299. }
  300. }
  301. goog.ui.tree.BaseNode.superClass_.removeChild.call(this, child);
  302. if (this.lastChild_ == child) {
  303. this.lastChild_ = child.previousSibling_;
  304. }
  305. if (this.firstChild_ == child) {
  306. this.firstChild_ = child.nextSibling_;
  307. }
  308. if (child.previousSibling_) {
  309. child.previousSibling_.nextSibling_ = child.nextSibling_;
  310. }
  311. if (child.nextSibling_) {
  312. child.nextSibling_.previousSibling_ = child.previousSibling_;
  313. }
  314. var wasLast = child.isLastSibling();
  315. child.tree = null;
  316. child.depth_ = -1;
  317. if (tree) {
  318. // Tell the tree control that the child node is now removed.
  319. tree.removeNode(child);
  320. if (this.isInDocument()) {
  321. var childrenEl = this.getChildrenElement();
  322. if (child.isInDocument()) {
  323. var childEl = child.getElement();
  324. childrenEl.removeChild(childEl);
  325. child.exitDocument();
  326. }
  327. if (wasLast) {
  328. var newLast = this.getLastChild();
  329. if (newLast) {
  330. newLast.updateExpandIcon();
  331. }
  332. }
  333. if (!this.hasChildren()) {
  334. childrenEl.style.display = 'none';
  335. this.updateExpandIcon();
  336. this.updateIcon_();
  337. var el = this.getElement();
  338. if (el) {
  339. goog.a11y.aria.removeState(el, goog.a11y.aria.State.EXPANDED);
  340. }
  341. }
  342. }
  343. }
  344. return child;
  345. };
  346. /**
  347. * @deprecated Use {@link #removeChild}.
  348. */
  349. goog.ui.tree.BaseNode.prototype.remove =
  350. goog.ui.tree.BaseNode.prototype.removeChild;
  351. /**
  352. * Handler for setting focus asynchronously.
  353. * @private
  354. */
  355. goog.ui.tree.BaseNode.prototype.onTimeoutSelect_ = function() {
  356. this.select();
  357. };
  358. /**
  359. * Returns the tree.
  360. * @return {?goog.ui.tree.TreeControl}
  361. */
  362. goog.ui.tree.BaseNode.prototype.getTree = goog.abstractMethod;
  363. /**
  364. * Returns the depth of the node in the tree. Should not be overridden.
  365. * @return {number} The non-negative depth of this node (the root is zero).
  366. */
  367. goog.ui.tree.BaseNode.prototype.getDepth = function() {
  368. var depth = this.depth_;
  369. if (depth < 0) {
  370. depth = this.computeDepth_();
  371. this.setDepth_(depth);
  372. }
  373. return depth;
  374. };
  375. /**
  376. * Computes the depth of the node in the tree.
  377. * Called only by getDepth, when the depth hasn't already been cached.
  378. * @return {number} The non-negative depth of this node (the root is zero).
  379. * @private
  380. */
  381. goog.ui.tree.BaseNode.prototype.computeDepth_ = function() {
  382. var parent = this.getParent();
  383. if (parent) {
  384. return parent.getDepth() + 1;
  385. } else {
  386. return 0;
  387. }
  388. };
  389. /**
  390. * Changes the depth of a node (and all its descendants).
  391. * @param {number} depth The new nesting depth; must be non-negative.
  392. * @private
  393. */
  394. goog.ui.tree.BaseNode.prototype.setDepth_ = function(depth) {
  395. if (depth != this.depth_) {
  396. this.depth_ = depth;
  397. var row = this.getRowElement();
  398. if (row) {
  399. var indent = this.getPixelIndent_() + 'px';
  400. if (this.isRightToLeft()) {
  401. row.style.paddingRight = indent;
  402. } else {
  403. row.style.paddingLeft = indent;
  404. }
  405. }
  406. this.forEachChild(function(child) { child.setDepth_(depth + 1); });
  407. }
  408. };
  409. /**
  410. * Returns true if the node is a descendant of this node
  411. * @param {goog.ui.tree.BaseNode} node The node to check.
  412. * @return {boolean} True if the node is a descendant of this node, false
  413. * otherwise.
  414. */
  415. goog.ui.tree.BaseNode.prototype.contains = function(node) {
  416. var current = node;
  417. while (current) {
  418. if (current == this) {
  419. return true;
  420. }
  421. current = current.getParent();
  422. }
  423. return false;
  424. };
  425. /**
  426. * An array of empty children to return for nodes that have no children.
  427. * @type {!Array<!goog.ui.tree.BaseNode>}
  428. * @private
  429. */
  430. goog.ui.tree.BaseNode.EMPTY_CHILDREN_ = [];
  431. /**
  432. * @param {number} index 0-based index.
  433. * @return {goog.ui.tree.BaseNode} The child at the given index; null if none.
  434. */
  435. goog.ui.tree.BaseNode.prototype.getChildAt;
  436. /**
  437. * Returns the children of this node.
  438. * @return {!Array<!goog.ui.tree.BaseNode>} The children.
  439. */
  440. goog.ui.tree.BaseNode.prototype.getChildren = function() {
  441. var children = [];
  442. this.forEachChild(function(child) { children.push(child); });
  443. return children;
  444. };
  445. /**
  446. * @return {goog.ui.tree.BaseNode} The first child of this node.
  447. */
  448. goog.ui.tree.BaseNode.prototype.getFirstChild = function() {
  449. return this.getChildAt(0);
  450. };
  451. /**
  452. * @return {goog.ui.tree.BaseNode} The last child of this node.
  453. */
  454. goog.ui.tree.BaseNode.prototype.getLastChild = function() {
  455. return this.getChildAt(this.getChildCount() - 1);
  456. };
  457. /**
  458. * @return {goog.ui.tree.BaseNode} The previous sibling of this node.
  459. */
  460. goog.ui.tree.BaseNode.prototype.getPreviousSibling = function() {
  461. return this.previousSibling_;
  462. };
  463. /**
  464. * @return {goog.ui.tree.BaseNode} The next sibling of this node.
  465. */
  466. goog.ui.tree.BaseNode.prototype.getNextSibling = function() {
  467. return this.nextSibling_;
  468. };
  469. /**
  470. * @return {boolean} Whether the node is the last sibling.
  471. */
  472. goog.ui.tree.BaseNode.prototype.isLastSibling = function() {
  473. return !this.nextSibling_;
  474. };
  475. /**
  476. * @return {boolean} Whether the node is selected.
  477. */
  478. goog.ui.tree.BaseNode.prototype.isSelected = function() {
  479. return this.selected_;
  480. };
  481. /**
  482. * Selects the node.
  483. */
  484. goog.ui.tree.BaseNode.prototype.select = function() {
  485. var tree = this.getTree();
  486. if (tree) {
  487. tree.setSelectedItem(this);
  488. }
  489. };
  490. /**
  491. * Originally it was intended to deselect the node but never worked.
  492. * @deprecated Use {@code tree.setSelectedItem(null)}.
  493. */
  494. goog.ui.tree.BaseNode.prototype.deselect = goog.nullFunction;
  495. /**
  496. * Called from the tree to instruct the node change its selection state.
  497. * @param {boolean} selected The new selection state.
  498. * @protected
  499. */
  500. goog.ui.tree.BaseNode.prototype.setSelectedInternal = function(selected) {
  501. if (this.selected_ == selected) {
  502. return;
  503. }
  504. this.selected_ = selected;
  505. this.updateRow();
  506. var el = this.getElement();
  507. if (el) {
  508. goog.a11y.aria.setState(el, 'selected', selected);
  509. if (selected) {
  510. var treeElement = this.getTree().getElement();
  511. goog.asserts.assert(
  512. treeElement, 'The DOM element for the tree cannot be null');
  513. goog.a11y.aria.setState(treeElement, 'activedescendant', this.getId());
  514. }
  515. }
  516. };
  517. /**
  518. * @return {boolean} Whether the node is expanded.
  519. */
  520. goog.ui.tree.BaseNode.prototype.getExpanded = function() {
  521. return this.expanded_;
  522. };
  523. /**
  524. * Sets the node to be expanded internally, without state change events.
  525. * @param {boolean} expanded Whether to expand or close the node.
  526. */
  527. goog.ui.tree.BaseNode.prototype.setExpandedInternal = function(expanded) {
  528. this.expanded_ = expanded;
  529. };
  530. /**
  531. * Sets the node to be expanded.
  532. * @param {boolean} expanded Whether to expand or close the node.
  533. */
  534. goog.ui.tree.BaseNode.prototype.setExpanded = function(expanded) {
  535. var isStateChange = expanded != this.expanded_;
  536. if (isStateChange) {
  537. // Only fire events if the expanded state has actually changed.
  538. var prevented = !this.dispatchEvent(
  539. expanded ? goog.ui.tree.BaseNode.EventType.BEFORE_EXPAND :
  540. goog.ui.tree.BaseNode.EventType.BEFORE_COLLAPSE);
  541. if (prevented) return;
  542. }
  543. var ce;
  544. this.expanded_ = expanded;
  545. var tree = this.getTree();
  546. var el = this.getElement();
  547. if (this.hasChildren()) {
  548. if (!expanded && tree && this.contains(tree.getSelectedItem())) {
  549. this.select();
  550. }
  551. if (el) {
  552. ce = this.getChildrenElement();
  553. if (ce) {
  554. goog.style.setElementShown(ce, expanded);
  555. goog.a11y.aria.setState(el, goog.a11y.aria.State.EXPANDED, expanded);
  556. // Make sure we have the HTML for the children here.
  557. if (expanded && this.isInDocument() && !ce.hasChildNodes()) {
  558. var children = [];
  559. this.forEachChild(function(child) {
  560. children.push(child.toSafeHtml());
  561. });
  562. goog.dom.safe.setInnerHtml(ce, goog.html.SafeHtml.concat(children));
  563. this.forEachChild(function(child) { child.enterDocument(); });
  564. }
  565. }
  566. this.updateExpandIcon();
  567. }
  568. } else {
  569. ce = this.getChildrenElement();
  570. if (ce) {
  571. goog.style.setElementShown(ce, false);
  572. }
  573. }
  574. if (el) {
  575. this.updateIcon_();
  576. }
  577. if (isStateChange) {
  578. this.dispatchEvent(
  579. expanded ? goog.ui.tree.BaseNode.EventType.EXPAND :
  580. goog.ui.tree.BaseNode.EventType.COLLAPSE);
  581. }
  582. };
  583. /**
  584. * Toggles the expanded state of the node.
  585. */
  586. goog.ui.tree.BaseNode.prototype.toggle = function() {
  587. this.setExpanded(!this.getExpanded());
  588. };
  589. /**
  590. * Expands the node.
  591. */
  592. goog.ui.tree.BaseNode.prototype.expand = function() {
  593. this.setExpanded(true);
  594. };
  595. /**
  596. * Collapses the node.
  597. */
  598. goog.ui.tree.BaseNode.prototype.collapse = function() {
  599. this.setExpanded(false);
  600. };
  601. /**
  602. * Collapses the children of the node.
  603. */
  604. goog.ui.tree.BaseNode.prototype.collapseChildren = function() {
  605. this.forEachChild(function(child) { child.collapseAll(); });
  606. };
  607. /**
  608. * Collapses the children and the node.
  609. */
  610. goog.ui.tree.BaseNode.prototype.collapseAll = function() {
  611. this.collapseChildren();
  612. this.collapse();
  613. };
  614. /**
  615. * Expands the children of the node.
  616. */
  617. goog.ui.tree.BaseNode.prototype.expandChildren = function() {
  618. this.forEachChild(function(child) { child.expandAll(); });
  619. };
  620. /**
  621. * Expands the children and the node.
  622. */
  623. goog.ui.tree.BaseNode.prototype.expandAll = function() {
  624. this.expandChildren();
  625. this.expand();
  626. };
  627. /**
  628. * Expands the parent chain of this node so that it is visible.
  629. */
  630. goog.ui.tree.BaseNode.prototype.reveal = function() {
  631. var parent = this.getParent();
  632. if (parent) {
  633. parent.setExpanded(true);
  634. parent.reveal();
  635. }
  636. };
  637. /**
  638. * Sets whether the node will allow the user to collapse it.
  639. * @param {boolean} isCollapsible Whether to allow node collapse.
  640. */
  641. goog.ui.tree.BaseNode.prototype.setIsUserCollapsible = function(isCollapsible) {
  642. this.isUserCollapsible_ = isCollapsible;
  643. if (!this.isUserCollapsible_) {
  644. this.expand();
  645. }
  646. if (this.getElement()) {
  647. this.updateExpandIcon();
  648. }
  649. };
  650. /**
  651. * @return {boolean} Whether the node is collapsible by user actions.
  652. */
  653. goog.ui.tree.BaseNode.prototype.isUserCollapsible = function() {
  654. return this.isUserCollapsible_;
  655. };
  656. /**
  657. * Creates HTML for the node.
  658. * @return {!goog.html.SafeHtml}
  659. * @protected
  660. */
  661. goog.ui.tree.BaseNode.prototype.toSafeHtml = function() {
  662. var tree = this.getTree();
  663. var hideLines = !tree.getShowLines() ||
  664. tree == this.getParent() && !tree.getShowRootLines();
  665. var childClass =
  666. hideLines ? this.config_.cssChildrenNoLines : this.config_.cssChildren;
  667. var nonEmptyAndExpanded = this.getExpanded() && this.hasChildren();
  668. var attributes = {'class': childClass, 'style': this.getLineStyle()};
  669. var content = [];
  670. if (nonEmptyAndExpanded) {
  671. // children
  672. this.forEachChild(function(child) { content.push(child.toSafeHtml()); });
  673. }
  674. var children = goog.html.SafeHtml.create('div', attributes, content);
  675. return goog.html.SafeHtml.create(
  676. 'div', {'class': this.config_.cssItem, 'id': this.getId()},
  677. [this.getRowSafeHtml(), children]);
  678. };
  679. /**
  680. * @return {number} The pixel indent of the row.
  681. * @private
  682. */
  683. goog.ui.tree.BaseNode.prototype.getPixelIndent_ = function() {
  684. return Math.max(0, (this.getDepth() - 1) * this.config_.indentWidth);
  685. };
  686. /**
  687. * @return {!goog.html.SafeHtml} The html for the row.
  688. * @protected
  689. */
  690. goog.ui.tree.BaseNode.prototype.getRowSafeHtml = function() {
  691. var style = {};
  692. style['padding-' + (this.isRightToLeft() ? 'right' : 'left')] =
  693. this.getPixelIndent_() + 'px';
  694. var attributes = {'class': this.getRowClassName(), 'style': style};
  695. var content = [
  696. this.getExpandIconSafeHtml(), this.getIconSafeHtml(),
  697. this.getLabelSafeHtml()
  698. ];
  699. return goog.html.SafeHtml.create('div', attributes, content);
  700. };
  701. /**
  702. * @return {string} The class name for the row.
  703. * @protected
  704. */
  705. goog.ui.tree.BaseNode.prototype.getRowClassName = function() {
  706. var selectedClass;
  707. if (this.isSelected()) {
  708. selectedClass = ' ' + this.config_.cssSelectedRow;
  709. } else {
  710. selectedClass = '';
  711. }
  712. return this.config_.cssTreeRow + selectedClass;
  713. };
  714. /**
  715. * @return {!goog.html.SafeHtml} The html for the label.
  716. * @protected
  717. */
  718. goog.ui.tree.BaseNode.prototype.getLabelSafeHtml = function() {
  719. var html = goog.html.SafeHtml.create(
  720. 'span',
  721. {'class': this.config_.cssItemLabel, 'title': this.getToolTip() || null},
  722. this.getSafeHtml());
  723. return goog.html.SafeHtml.concat(
  724. html,
  725. goog.html.SafeHtml.create('span', {}, this.getAfterLabelSafeHtml()));
  726. };
  727. /**
  728. * Returns the html that appears after the label. This is useful if you want to
  729. * put extra UI on the row of the label but not inside the anchor tag.
  730. * @return {string} The html.
  731. * @final
  732. */
  733. goog.ui.tree.BaseNode.prototype.getAfterLabelHtml = function() {
  734. return goog.html.SafeHtml.unwrap(this.getAfterLabelSafeHtml());
  735. };
  736. /**
  737. * Returns the html that appears after the label. This is useful if you want to
  738. * put extra UI on the row of the label but not inside the anchor tag.
  739. * @return {!goog.html.SafeHtml} The html.
  740. */
  741. goog.ui.tree.BaseNode.prototype.getAfterLabelSafeHtml = function() {
  742. return this.afterLabelHtml_;
  743. };
  744. /**
  745. * Sets the html that appears after the label. This is useful if you want to
  746. * put extra UI on the row of the label but not inside the anchor tag.
  747. * @param {!goog.html.SafeHtml} html The html.
  748. */
  749. goog.ui.tree.BaseNode.prototype.setAfterLabelSafeHtml = function(html) {
  750. this.afterLabelHtml_ = html;
  751. var el = this.getAfterLabelElement();
  752. if (el) {
  753. goog.dom.safe.setInnerHtml(el, html);
  754. }
  755. };
  756. /**
  757. * @return {!goog.html.SafeHtml} The html for the icon.
  758. * @protected
  759. */
  760. goog.ui.tree.BaseNode.prototype.getIconSafeHtml = function() {
  761. return goog.html.SafeHtml.create('span', {
  762. 'style': {'display': 'inline-block'},
  763. 'class': this.getCalculatedIconClass()
  764. });
  765. };
  766. /**
  767. * Gets the calculated icon class.
  768. * @protected
  769. */
  770. goog.ui.tree.BaseNode.prototype.getCalculatedIconClass = goog.abstractMethod;
  771. /**
  772. * @return {!goog.html.SafeHtml} The source for the icon.
  773. * @protected
  774. */
  775. goog.ui.tree.BaseNode.prototype.getExpandIconSafeHtml = function() {
  776. return goog.html.SafeHtml.create('span', {
  777. 'type': 'expand',
  778. 'style': {'display': 'inline-block'},
  779. 'class': this.getExpandIconClass()
  780. });
  781. };
  782. /**
  783. * @return {string} The class names of the icon used for expanding the node.
  784. * @protected
  785. */
  786. goog.ui.tree.BaseNode.prototype.getExpandIconClass = function() {
  787. var tree = this.getTree();
  788. var hideLines = !tree.getShowLines() ||
  789. tree == this.getParent() && !tree.getShowRootLines();
  790. var config = this.config_;
  791. var sb = new goog.string.StringBuffer();
  792. sb.append(config.cssTreeIcon, ' ', config.cssExpandTreeIcon, ' ');
  793. if (this.hasChildren()) {
  794. var bits = 0;
  795. /*
  796. Bitmap used to determine which icon to use
  797. 1 Plus
  798. 2 Minus
  799. 4 T Line
  800. 8 L Line
  801. */
  802. if (tree.getShowExpandIcons() && this.isUserCollapsible_) {
  803. if (this.getExpanded()) {
  804. bits = 2;
  805. } else {
  806. bits = 1;
  807. }
  808. }
  809. if (!hideLines) {
  810. if (this.isLastSibling()) {
  811. bits += 4;
  812. } else {
  813. bits += 8;
  814. }
  815. }
  816. switch (bits) {
  817. case 1:
  818. sb.append(config.cssExpandTreeIconPlus);
  819. break;
  820. case 2:
  821. sb.append(config.cssExpandTreeIconMinus);
  822. break;
  823. case 4:
  824. sb.append(config.cssExpandTreeIconL);
  825. break;
  826. case 5:
  827. sb.append(config.cssExpandTreeIconLPlus);
  828. break;
  829. case 6:
  830. sb.append(config.cssExpandTreeIconLMinus);
  831. break;
  832. case 8:
  833. sb.append(config.cssExpandTreeIconT);
  834. break;
  835. case 9:
  836. sb.append(config.cssExpandTreeIconTPlus);
  837. break;
  838. case 10:
  839. sb.append(config.cssExpandTreeIconTMinus);
  840. break;
  841. default: // 0
  842. sb.append(config.cssExpandTreeIconBlank);
  843. }
  844. } else {
  845. if (hideLines) {
  846. sb.append(config.cssExpandTreeIconBlank);
  847. } else if (this.isLastSibling()) {
  848. sb.append(config.cssExpandTreeIconL);
  849. } else {
  850. sb.append(config.cssExpandTreeIconT);
  851. }
  852. }
  853. return sb.toString();
  854. };
  855. /**
  856. * @return {!goog.html.SafeStyle} The line style.
  857. */
  858. goog.ui.tree.BaseNode.prototype.getLineStyle = function() {
  859. var nonEmptyAndExpanded = this.getExpanded() && this.hasChildren();
  860. return goog.html.SafeStyle.create({
  861. 'background-position': this.getBackgroundPosition(),
  862. 'display': nonEmptyAndExpanded ? null : 'none'
  863. });
  864. };
  865. /**
  866. * @return {string} The background position style value.
  867. */
  868. goog.ui.tree.BaseNode.prototype.getBackgroundPosition = function() {
  869. return (this.isLastSibling() ? '-100' : (this.getDepth() - 1) *
  870. this.config_.indentWidth) +
  871. 'px 0';
  872. };
  873. /**
  874. * @return {Element} The element for the tree node.
  875. * @override
  876. */
  877. goog.ui.tree.BaseNode.prototype.getElement = function() {
  878. var el = goog.ui.tree.BaseNode.superClass_.getElement.call(this);
  879. if (!el) {
  880. el = this.getDomHelper().getElement(this.getId());
  881. this.setElementInternal(el);
  882. }
  883. return el;
  884. };
  885. /**
  886. * @return {Element} The row is the div that is used to draw the node without
  887. * the children.
  888. */
  889. goog.ui.tree.BaseNode.prototype.getRowElement = function() {
  890. var el = this.getElement();
  891. return el ? /** @type {Element} */ (el.firstChild) : null;
  892. };
  893. /**
  894. * @return {Element} The expanded icon element.
  895. * @protected
  896. */
  897. goog.ui.tree.BaseNode.prototype.getExpandIconElement = function() {
  898. var el = this.getRowElement();
  899. return el ? /** @type {Element} */ (el.firstChild) : null;
  900. };
  901. /**
  902. * @return {Element} The icon element.
  903. * @protected
  904. */
  905. goog.ui.tree.BaseNode.prototype.getIconElement = function() {
  906. var el = this.getRowElement();
  907. return el ? /** @type {Element} */ (el.childNodes[1]) : null;
  908. };
  909. /**
  910. * @return {Element} The label element.
  911. */
  912. goog.ui.tree.BaseNode.prototype.getLabelElement = function() {
  913. var el = this.getRowElement();
  914. // TODO: find/fix race condition that requires us to add
  915. // the lastChild check
  916. return el && el.lastChild ?
  917. /** @type {Element} */ (el.lastChild.previousSibling) :
  918. null;
  919. };
  920. /**
  921. * @return {Element} The element after the label.
  922. */
  923. goog.ui.tree.BaseNode.prototype.getAfterLabelElement = function() {
  924. var el = this.getRowElement();
  925. return el ? /** @type {Element} */ (el.lastChild) : null;
  926. };
  927. /**
  928. * @return {Element} The div containing the children.
  929. * @protected
  930. */
  931. goog.ui.tree.BaseNode.prototype.getChildrenElement = function() {
  932. var el = this.getElement();
  933. return el ? /** @type {Element} */ (el.lastChild) : null;
  934. };
  935. /**
  936. * Sets the icon class for the node.
  937. * @param {string} s The icon class.
  938. */
  939. goog.ui.tree.BaseNode.prototype.setIconClass = function(s) {
  940. this.iconClass_ = s;
  941. if (this.isInDocument()) {
  942. this.updateIcon_();
  943. }
  944. };
  945. /**
  946. * Gets the icon class for the node.
  947. * @return {string} s The icon source.
  948. */
  949. goog.ui.tree.BaseNode.prototype.getIconClass = function() {
  950. return this.iconClass_;
  951. };
  952. /**
  953. * Sets the icon class for when the node is expanded.
  954. * @param {string} s The expanded icon class.
  955. */
  956. goog.ui.tree.BaseNode.prototype.setExpandedIconClass = function(s) {
  957. this.expandedIconClass_ = s;
  958. if (this.isInDocument()) {
  959. this.updateIcon_();
  960. }
  961. };
  962. /**
  963. * Gets the icon class for when the node is expanded.
  964. * @return {string} The class.
  965. */
  966. goog.ui.tree.BaseNode.prototype.getExpandedIconClass = function() {
  967. return this.expandedIconClass_;
  968. };
  969. /**
  970. * Sets the text of the label.
  971. * @param {string} s The plain text of the label.
  972. */
  973. goog.ui.tree.BaseNode.prototype.setText = function(s) {
  974. this.setSafeHtml(goog.html.SafeHtml.htmlEscape(s));
  975. };
  976. /**
  977. * Returns the text of the label. If the text was originally set as HTML, the
  978. * return value is unspecified.
  979. * @return {string} The plain text of the label.
  980. */
  981. goog.ui.tree.BaseNode.prototype.getText = function() {
  982. return goog.string.unescapeEntities(goog.html.SafeHtml.unwrap(this.html_));
  983. };
  984. /**
  985. * Sets the HTML of the label.
  986. * @param {!goog.html.SafeHtml} html The HTML object for the label.
  987. */
  988. goog.ui.tree.BaseNode.prototype.setSafeHtml = function(html) {
  989. this.html_ = html;
  990. var el = this.getLabelElement();
  991. if (el) {
  992. goog.dom.safe.setInnerHtml(el, html);
  993. }
  994. var tree = this.getTree();
  995. if (tree) {
  996. // Tell the tree control about the updated label text.
  997. tree.setNode(this);
  998. }
  999. };
  1000. /**
  1001. * Returns the html of the label.
  1002. * @return {string} The html string of the label.
  1003. * @final
  1004. */
  1005. goog.ui.tree.BaseNode.prototype.getHtml = function() {
  1006. return goog.html.SafeHtml.unwrap(this.getSafeHtml());
  1007. };
  1008. /**
  1009. * Returns the html of the label.
  1010. * @return {!goog.html.SafeHtml} The html string of the label.
  1011. */
  1012. goog.ui.tree.BaseNode.prototype.getSafeHtml = function() {
  1013. return this.html_;
  1014. };
  1015. /**
  1016. * Sets the text of the tooltip.
  1017. * @param {string} s The tooltip text to set.
  1018. */
  1019. goog.ui.tree.BaseNode.prototype.setToolTip = function(s) {
  1020. this.toolTip_ = s;
  1021. var el = this.getLabelElement();
  1022. if (el) {
  1023. el.title = s;
  1024. }
  1025. };
  1026. /**
  1027. * Returns the text of the tooltip.
  1028. * @return {?string} The tooltip text.
  1029. */
  1030. goog.ui.tree.BaseNode.prototype.getToolTip = function() {
  1031. return this.toolTip_;
  1032. };
  1033. /**
  1034. * Updates the row styles.
  1035. */
  1036. goog.ui.tree.BaseNode.prototype.updateRow = function() {
  1037. var rowEl = this.getRowElement();
  1038. if (rowEl) {
  1039. rowEl.className = this.getRowClassName();
  1040. }
  1041. };
  1042. /**
  1043. * Updates the expand icon of the node.
  1044. */
  1045. goog.ui.tree.BaseNode.prototype.updateExpandIcon = function() {
  1046. var img = this.getExpandIconElement();
  1047. if (img) {
  1048. img.className = this.getExpandIconClass();
  1049. }
  1050. var cel = this.getChildrenElement();
  1051. if (cel) {
  1052. cel.style.backgroundPosition = this.getBackgroundPosition();
  1053. }
  1054. };
  1055. /**
  1056. * Updates the icon of the node. Assumes that this.getElement() is created.
  1057. * @private
  1058. */
  1059. goog.ui.tree.BaseNode.prototype.updateIcon_ = function() {
  1060. this.getIconElement().className = this.getCalculatedIconClass();
  1061. };
  1062. /**
  1063. * Handles mouse down event.
  1064. * @param {!goog.events.BrowserEvent} e The browser event.
  1065. * @protected
  1066. */
  1067. goog.ui.tree.BaseNode.prototype.onMouseDown = function(e) {
  1068. var el = e.target;
  1069. // expand icon
  1070. var type = el.getAttribute('type');
  1071. if (type == 'expand' && this.hasChildren()) {
  1072. if (this.isUserCollapsible_) {
  1073. this.toggle();
  1074. }
  1075. return;
  1076. }
  1077. this.select();
  1078. this.updateRow();
  1079. };
  1080. /**
  1081. * Handles a click event.
  1082. * @param {!goog.events.BrowserEvent} e The browser event.
  1083. * @protected
  1084. * @suppress {underscore|visibility}
  1085. */
  1086. goog.ui.tree.BaseNode.prototype.onClick_ = goog.events.Event.preventDefault;
  1087. /**
  1088. * Handles a double click event.
  1089. * @param {!goog.events.BrowserEvent} e The browser event.
  1090. * @protected
  1091. * @suppress {underscore|visibility}
  1092. */
  1093. goog.ui.tree.BaseNode.prototype.onDoubleClick_ = function(e) {
  1094. var el = e.target;
  1095. // expand icon
  1096. var type = el.getAttribute('type');
  1097. if (type == 'expand' && this.hasChildren()) {
  1098. return;
  1099. }
  1100. if (this.isUserCollapsible_) {
  1101. this.toggle();
  1102. }
  1103. };
  1104. /**
  1105. * Handles a key down event.
  1106. * @param {!goog.events.BrowserEvent} e The browser event.
  1107. * @return {boolean} The handled value.
  1108. * @protected
  1109. */
  1110. goog.ui.tree.BaseNode.prototype.onKeyDown = function(e) {
  1111. var handled = true;
  1112. switch (e.keyCode) {
  1113. case goog.events.KeyCodes.RIGHT:
  1114. if (e.altKey) {
  1115. break;
  1116. }
  1117. if (this.hasChildren()) {
  1118. if (!this.getExpanded()) {
  1119. this.setExpanded(true);
  1120. } else {
  1121. this.getFirstChild().select();
  1122. }
  1123. }
  1124. break;
  1125. case goog.events.KeyCodes.LEFT:
  1126. if (e.altKey) {
  1127. break;
  1128. }
  1129. if (this.hasChildren() && this.getExpanded() && this.isUserCollapsible_) {
  1130. this.setExpanded(false);
  1131. } else {
  1132. var parent = this.getParent();
  1133. var tree = this.getTree();
  1134. // don't go to root if hidden
  1135. if (parent && (tree.getShowRootNode() || parent != tree)) {
  1136. parent.select();
  1137. }
  1138. }
  1139. break;
  1140. case goog.events.KeyCodes.DOWN:
  1141. var nextNode = this.getNextShownNode();
  1142. if (nextNode) {
  1143. nextNode.select();
  1144. }
  1145. break;
  1146. case goog.events.KeyCodes.UP:
  1147. var previousNode = this.getPreviousShownNode();
  1148. if (previousNode) {
  1149. previousNode.select();
  1150. }
  1151. break;
  1152. default:
  1153. handled = false;
  1154. }
  1155. if (handled) {
  1156. e.preventDefault();
  1157. var tree = this.getTree();
  1158. if (tree) {
  1159. // clear type ahead buffer as user navigates with arrow keys
  1160. tree.clearTypeAhead();
  1161. }
  1162. }
  1163. return handled;
  1164. };
  1165. /**
  1166. * @return {goog.ui.tree.BaseNode} The last shown descendant.
  1167. */
  1168. goog.ui.tree.BaseNode.prototype.getLastShownDescendant = function() {
  1169. if (!this.getExpanded() || !this.hasChildren()) {
  1170. return this;
  1171. }
  1172. // we know there is at least 1 child
  1173. return this.getLastChild().getLastShownDescendant();
  1174. };
  1175. /**
  1176. * @return {goog.ui.tree.BaseNode} The next node to show or null if there isn't
  1177. * a next node to show.
  1178. */
  1179. goog.ui.tree.BaseNode.prototype.getNextShownNode = function() {
  1180. if (this.hasChildren() && this.getExpanded()) {
  1181. return this.getFirstChild();
  1182. } else {
  1183. var parent = this;
  1184. var next;
  1185. while (parent != this.getTree()) {
  1186. next = parent.getNextSibling();
  1187. if (next != null) {
  1188. return next;
  1189. }
  1190. parent = parent.getParent();
  1191. }
  1192. return null;
  1193. }
  1194. };
  1195. /**
  1196. * @return {goog.ui.tree.BaseNode} The previous node to show.
  1197. */
  1198. goog.ui.tree.BaseNode.prototype.getPreviousShownNode = function() {
  1199. var ps = this.getPreviousSibling();
  1200. if (ps != null) {
  1201. return ps.getLastShownDescendant();
  1202. }
  1203. var parent = this.getParent();
  1204. var tree = this.getTree();
  1205. if (!tree.getShowRootNode() && parent == tree) {
  1206. return null;
  1207. }
  1208. // The root is the first node.
  1209. if (this == tree) {
  1210. return null;
  1211. }
  1212. return /** @type {goog.ui.tree.BaseNode} */ (parent);
  1213. };
  1214. /**
  1215. * @return {*} Data set by the client.
  1216. * @deprecated Use {@link #getModel} instead.
  1217. */
  1218. goog.ui.tree.BaseNode.prototype.getClientData =
  1219. goog.ui.tree.BaseNode.prototype.getModel;
  1220. /**
  1221. * Sets client data to associate with the node.
  1222. * @param {*} data The client data to associate with the node.
  1223. * @deprecated Use {@link #setModel} instead.
  1224. */
  1225. goog.ui.tree.BaseNode.prototype.setClientData =
  1226. goog.ui.tree.BaseNode.prototype.setModel;
  1227. /**
  1228. * @return {Object} The configuration for the tree.
  1229. */
  1230. goog.ui.tree.BaseNode.prototype.getConfig = function() {
  1231. return this.config_;
  1232. };
  1233. /**
  1234. * Internal method that is used to set the tree control on the node.
  1235. * @param {goog.ui.tree.TreeControl} tree The tree control.
  1236. */
  1237. goog.ui.tree.BaseNode.prototype.setTreeInternal = function(tree) {
  1238. if (this.tree != tree) {
  1239. this.tree = tree;
  1240. // Add new node to the type ahead node map.
  1241. tree.setNode(this);
  1242. this.forEachChild(function(child) { child.setTreeInternal(tree); });
  1243. }
  1244. };
  1245. /**
  1246. * A default configuration for the tree.
  1247. */
  1248. goog.ui.tree.BaseNode.defaultConfig = {
  1249. indentWidth: 19,
  1250. cssRoot: goog.getCssName('goog-tree-root') + ' ' +
  1251. goog.getCssName('goog-tree-item'),
  1252. cssHideRoot: goog.getCssName('goog-tree-hide-root'),
  1253. cssItem: goog.getCssName('goog-tree-item'),
  1254. cssChildren: goog.getCssName('goog-tree-children'),
  1255. cssChildrenNoLines: goog.getCssName('goog-tree-children-nolines'),
  1256. cssTreeRow: goog.getCssName('goog-tree-row'),
  1257. cssItemLabel: goog.getCssName('goog-tree-item-label'),
  1258. cssTreeIcon: goog.getCssName('goog-tree-icon'),
  1259. cssExpandTreeIcon: goog.getCssName('goog-tree-expand-icon'),
  1260. cssExpandTreeIconPlus: goog.getCssName('goog-tree-expand-icon-plus'),
  1261. cssExpandTreeIconMinus: goog.getCssName('goog-tree-expand-icon-minus'),
  1262. cssExpandTreeIconTPlus: goog.getCssName('goog-tree-expand-icon-tplus'),
  1263. cssExpandTreeIconTMinus: goog.getCssName('goog-tree-expand-icon-tminus'),
  1264. cssExpandTreeIconLPlus: goog.getCssName('goog-tree-expand-icon-lplus'),
  1265. cssExpandTreeIconLMinus: goog.getCssName('goog-tree-expand-icon-lminus'),
  1266. cssExpandTreeIconT: goog.getCssName('goog-tree-expand-icon-t'),
  1267. cssExpandTreeIconL: goog.getCssName('goog-tree-expand-icon-l'),
  1268. cssExpandTreeIconBlank: goog.getCssName('goog-tree-expand-icon-blank'),
  1269. cssExpandedFolderIcon: goog.getCssName('goog-tree-expanded-folder-icon'),
  1270. cssCollapsedFolderIcon: goog.getCssName('goog-tree-collapsed-folder-icon'),
  1271. cssFileIcon: goog.getCssName('goog-tree-file-icon'),
  1272. cssExpandedRootIcon: goog.getCssName('goog-tree-expanded-folder-icon'),
  1273. cssCollapsedRootIcon: goog.getCssName('goog-tree-collapsed-folder-icon'),
  1274. cssSelectedRow: goog.getCssName('selected')
  1275. };