d4aff45a0f644f42add34c4fa8c5218d87846f44.svn-base 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. /*
  2. * Ext JS Library 2.0 Copyright(c) 2006-2007, Ext JS, LLC. licensing@extjs.com
  3. *
  4. * http://extjs.com/license
  5. */
  6. /**
  7. * @class Ext.tree.TreeNodeUI This class provides the default UI implementation
  8. * for Ext TreeNodes. The TreeNode UI implementation is separate from the
  9. * tree implementation, and allows customizing of the appearance of tree
  10. * nodes.<br>
  11. * <p>
  12. * If you are customizing the Tree's user interface, you may need to
  13. * extend this class, but you should never need to instantiate this
  14. * class.<br>
  15. * <p>
  16. * This class provides access to the user interface components of an Ext
  17. * TreeNode, through {@link Ext.tree.TreeNode#getUI}
  18. */
  19. Ext.tree.TreeNodeUI = function(node) {
  20. this.node = node;
  21. this.rendered = false;
  22. this.animating = false;
  23. this.wasLeaf = true;
  24. this.ecc = 'x-tree-ec-icon x-tree-elbow';
  25. this.emptyIcon = Ext.BLANK_IMAGE_URL;
  26. };
  27. Ext.tree.TreeNodeUI.prototype = {
  28. // private
  29. removeChild : function(node) {
  30. if (this.rendered) {
  31. this.ctNode.removeChild(node.ui.getEl());
  32. }
  33. },
  34. // private
  35. beforeLoad : function() {
  36. this.addClass("x-tree-node-loading");
  37. },
  38. // private
  39. afterLoad : function() {
  40. this.removeClass("x-tree-node-loading");
  41. },
  42. // private
  43. onTextChange : function(node, text, oldText) {
  44. if (this.rendered) {
  45. this.textNode.innerHTML = text;
  46. }
  47. },
  48. // private
  49. onDisableChange : function(node, state) {
  50. this.disabled = state;
  51. if (state) {
  52. this.addClass("x-tree-node-disabled");
  53. } else {
  54. this.removeClass("x-tree-node-disabled");
  55. }
  56. },
  57. // private
  58. onSelectedChange : function(state) {
  59. if (state) {
  60. this.focus();
  61. this.addClass("x-tree-selected");
  62. } else {
  63. // this.blur();
  64. this.removeClass("x-tree-selected");
  65. }
  66. },
  67. // private
  68. onMove : function(tree, node, oldParent, newParent, index, refNode) {
  69. this.childIndent = null;
  70. if (this.rendered) {
  71. var targetNode = newParent.ui.getContainer();
  72. if (!targetNode) {// target not rendered
  73. this.holder = document.createElement("div");
  74. this.holder.appendChild(this.wrap);
  75. return;
  76. }
  77. var insertBefore = refNode ? refNode.ui.getEl() : null;
  78. if (insertBefore) {
  79. targetNode.insertBefore(this.wrap, insertBefore);
  80. } else {
  81. targetNode.appendChild(this.wrap);
  82. }
  83. this.node.renderIndent(true);
  84. }
  85. },
  86. /**
  87. * Adds one or more CSS classes to the node's UI element. Duplicate classes
  88. * are automatically filtered out.
  89. *
  90. * @param {String/Array}
  91. * className The CSS class to add, or an array of classes
  92. */
  93. addClass : function(cls) {
  94. if (this.elNode) {
  95. Ext.fly(this.elNode).addClass(cls);
  96. }
  97. },
  98. /**
  99. * Removes one or more CSS classes from the node's UI element.
  100. *
  101. * @param {String/Array}
  102. * className The CSS class to remove, or an array of classes
  103. */
  104. removeClass : function(cls) {
  105. if (this.elNode) {
  106. Ext.fly(this.elNode).removeClass(cls);
  107. }
  108. },
  109. // private
  110. remove : function() {
  111. if (this.rendered) {
  112. this.holder = document.createElement("div");
  113. this.holder.appendChild(this.wrap);
  114. }
  115. },
  116. // private
  117. fireEvent : function() {
  118. return this.node.fireEvent.apply(this.node, arguments);
  119. },
  120. // private
  121. initEvents : function() {
  122. this.node.on("move", this.onMove, this);
  123. if (this.node.disabled) {
  124. this.addClass("x-tree-node-disabled");
  125. }
  126. if (this.node.hidden) {
  127. this.hide();
  128. }
  129. var ot = this.node.getOwnerTree();
  130. var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
  131. if (dd && (!this.node.isRoot || ot.rootVisible)) {
  132. Ext.dd.Registry.register(this.elNode, {
  133. node : this.node,
  134. handles : this.getDDHandles(),
  135. isHandle : false
  136. });
  137. }
  138. },
  139. // private
  140. getDDHandles : function() {
  141. return [this.iconNode, this.textNode, this.elNode];
  142. },
  143. /**
  144. * Hides this node.
  145. */
  146. hide : function() {
  147. this.node.hidden = true;
  148. if (this.wrap) {
  149. this.wrap.style.display = "none";
  150. }
  151. },
  152. /**
  153. * Shows this node.
  154. */
  155. show : function() {
  156. this.node.hidden = false;
  157. if (this.wrap) {
  158. this.wrap.style.display = "";
  159. }
  160. },
  161. // private
  162. onContextMenu : function(e) {
  163. if (this.node.hasListener("contextmenu")
  164. || this.node.getOwnerTree().hasListener("contextmenu")) {
  165. e.preventDefault();
  166. this.focus();
  167. this.fireEvent("contextmenu", this.node, e);
  168. }
  169. },
  170. // private
  171. onClick : function(e) {
  172. if (this.dropping) {
  173. e.stopEvent();
  174. return;
  175. }
  176. if (this.fireEvent("beforeclick", this.node, e) !== false) {
  177. var a = e.getTarget('a');
  178. if (!this.disabled && this.node.attributes.href && a) {
  179. this.fireEvent("click", this.node, e);
  180. return;
  181. } else if (a && e.ctrlKey) {
  182. e.stopEvent();
  183. }
  184. e.preventDefault();
  185. if (this.disabled) {
  186. return;
  187. }
  188. if (this.node.attributes.singleClickExpand && !this.animating
  189. && this.node.hasChildNodes()) {
  190. this.node.toggle();
  191. }
  192. this.fireEvent("click", this.node, e);
  193. } else {
  194. e.stopEvent();
  195. }
  196. },
  197. // private
  198. onDblClick : function(e) {
  199. e.preventDefault();
  200. if (this.disabled) {
  201. return;
  202. }
  203. if (this.checkbox) {
  204. this.toggleCheck();
  205. }
  206. if (!this.animating && this.node.hasChildNodes()) {
  207. this.node.toggle();
  208. }
  209. this.fireEvent("dblclick", this.node, e);
  210. },
  211. onOver : function(e) {
  212. this.addClass('x-tree-node-over');
  213. },
  214. onOut : function(e) {
  215. this.removeClass('x-tree-node-over');
  216. },
  217. // private
  218. onCheckChange : function() {
  219. var checked = this.checkbox.checked;
  220. this.node.attributes.checked = checked;
  221. this.fireEvent('checkchange', this.node, checked);
  222. },
  223. // private
  224. ecClick : function(e) {
  225. if (!this.animating
  226. && (this.node.hasChildNodes() || this.node.attributes.expandable)) {
  227. this.node.toggle();
  228. }
  229. },
  230. // private
  231. startDrop : function() {
  232. this.dropping = true;
  233. },
  234. // delayed drop so the click event doesn't get fired on a drop
  235. endDrop : function() {
  236. setTimeout(function() {
  237. this.dropping = false;
  238. }.createDelegate(this), 50);
  239. },
  240. // private
  241. expand : function() {
  242. this.updateExpandIcon();
  243. this.ctNode.style.display = "";
  244. },
  245. // private
  246. focus : function() {
  247. if (!this.node.preventHScroll) {
  248. try {
  249. this.anchor.focus();
  250. } catch (e) {
  251. }
  252. } else if (!Ext.isIE) {
  253. try {
  254. var noscroll = this.node.getOwnerTree().getTreeEl().dom;
  255. var l = noscroll.scrollLeft;
  256. this.anchor.focus();
  257. noscroll.scrollLeft = l;
  258. } catch (e) {
  259. }
  260. }
  261. },
  262. /**
  263. * Sets the checked status of the tree node to the passed value, or, if no
  264. * value was passed, toggles the checked status. If the node was rendered
  265. * with no checkbox, this has no effect.
  266. *
  267. * @param {Boolean}
  268. * (optional) The new checked status.
  269. */
  270. toggleCheck : function(value) {
  271. var cb = this.checkbox;
  272. if (cb) {
  273. cb.checked = (value === undefined ? !cb.checked : value);
  274. }
  275. },
  276. // private
  277. blur : function() {
  278. try {
  279. this.anchor.blur();
  280. } catch (e) {
  281. }
  282. },
  283. // private
  284. animExpand : function(callback) {
  285. var ct = Ext.get(this.ctNode);
  286. ct.stopFx();
  287. if (!this.node.hasChildNodes()) {
  288. this.updateExpandIcon();
  289. this.ctNode.style.display = "";
  290. Ext.callback(callback);
  291. return;
  292. }
  293. this.animating = true;
  294. this.updateExpandIcon();
  295. ct.slideIn('t', {
  296. callback : function() {
  297. this.animating = false;
  298. Ext.callback(callback);
  299. },
  300. scope : this,
  301. duration : this.node.ownerTree.duration || .25
  302. });
  303. },
  304. // private
  305. highlight : function() {
  306. var tree = this.node.getOwnerTree();
  307. Ext.fly(this.wrap).highlight(tree.hlColor || "C3DAF9", {
  308. endColor : tree.hlBaseColor
  309. });
  310. },
  311. // private
  312. collapse : function() {
  313. this.updateExpandIcon();
  314. this.ctNode.style.display = "none";
  315. },
  316. // private
  317. animCollapse : function(callback) {
  318. var ct = Ext.get(this.ctNode);
  319. ct.enableDisplayMode('block');
  320. ct.stopFx();
  321. this.animating = true;
  322. this.updateExpandIcon();
  323. ct.slideOut('t', {
  324. callback : function() {
  325. this.animating = false;
  326. Ext.callback(callback);
  327. },
  328. scope : this,
  329. duration : this.node.ownerTree.duration || .25
  330. });
  331. },
  332. // private
  333. getContainer : function() {
  334. return this.ctNode;
  335. },
  336. // private
  337. getEl : function() {
  338. return this.wrap;
  339. },
  340. // private
  341. appendDDGhost : function(ghostNode) {
  342. ghostNode.appendChild(this.elNode.cloneNode(true));
  343. },
  344. // private
  345. getDDRepairXY : function() {
  346. return Ext.lib.Dom.getXY(this.iconNode);
  347. },
  348. // private
  349. onRender : function() {
  350. this.render();
  351. },
  352. // private
  353. render : function(bulkRender) {
  354. var n = this.node, a = n.attributes;
  355. var targetNode = n.parentNode
  356. ? n.parentNode.ui.getContainer()
  357. : n.ownerTree.innerCt.dom;
  358. if (!this.rendered) {
  359. this.rendered = true;
  360. this.renderElements(n, a, targetNode, bulkRender);
  361. if (a.qtip) {
  362. if (this.textNode.setAttributeNS) {
  363. this.textNode.setAttributeNS("ext", "qtip", a.qtip);
  364. if (a.qtipTitle) {
  365. this.textNode.setAttributeNS("ext", "qtitle",
  366. a.qtipTitle);
  367. }
  368. } else {
  369. this.textNode.setAttribute("ext:qtip", a.qtip);
  370. if (a.qtipTitle) {
  371. this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
  372. }
  373. }
  374. } else if (a.qtipCfg) {
  375. a.qtipCfg.target = Ext.id(this.textNode);
  376. Ext.QuickTips.register(a.qtipCfg);
  377. }
  378. this.initEvents();
  379. if (!this.node.expanded) {
  380. this.updateExpandIcon(true);
  381. }
  382. } else {
  383. if (bulkRender === true) {
  384. targetNode.appendChild(this.wrap);
  385. }
  386. }
  387. },
  388. // private
  389. renderElements : function(n, a, targetNode, bulkRender) {
  390. // add some indent caching, this helps performance when rendering a
  391. // large tree
  392. this.indentMarkup = n.parentNode
  393. ? n.parentNode.ui.getChildIndent()
  394. : '';
  395. var cb = typeof a.checked == 'boolean';
  396. var href = a.href ? a.href : Ext.isGecko ? "" : "#";
  397. var buf = [
  398. '<li class="x-tree-node"><div ext:tree-node-id="',
  399. n.id,
  400. '" class="x-tree-node-el x-tree-node-leaf x-unselectable ',
  401. a.cls,
  402. '" unselectable="on">',
  403. '<span class="x-tree-node-indent">',
  404. this.indentMarkup,
  405. "</span>",
  406. '<img src="',
  407. this.emptyIcon,
  408. '" class="x-tree-ec-icon x-tree-elbow" />',
  409. '<img src="',
  410. a.icon || this.emptyIcon,
  411. '" class="x-tree-node-icon',
  412. (a.icon ? " x-tree-node-inline-icon" : ""),
  413. (a.iconCls ? " " + a.iconCls : ""),
  414. '" unselectable="on" />',
  415. cb
  416. ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked
  417. ? 'checked="checked" />'
  418. : '/>'))
  419. : '',
  420. '<a hidefocus="on" class="x-tree-node-anchor" href="', href,
  421. '" tabIndex="1" ',
  422. a.hrefTarget ? ' target="' + a.hrefTarget + '"' : "",
  423. '><span unselectable="on">', n.text, "</span></a></div>",
  424. '<ul class="x-tree-node-ct" style="display:none;"></ul>',
  425. "</li>"].join('');
  426. var nel;
  427. if (bulkRender !== true && n.nextSibling
  428. && (nel = n.nextSibling.ui.getEl())) {
  429. this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);
  430. } else {
  431. this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);
  432. }
  433. this.elNode = this.wrap.childNodes[0];
  434. this.ctNode = this.wrap.childNodes[1];
  435. var cs = this.elNode.childNodes;
  436. this.indentNode = cs[0];
  437. this.ecNode = cs[1];
  438. this.iconNode = cs[2];
  439. var index = 3;
  440. if (cb) {
  441. this.checkbox = cs[3];
  442. index++;
  443. }
  444. this.anchor = cs[index];
  445. this.textNode = cs[index].firstChild;
  446. },
  447. /**
  448. * Returns the &lt;a> element that provides focus for the node's UI.
  449. *
  450. * @return {HtmlElement} The DOM anchor element.
  451. */
  452. getAnchor : function() {
  453. return this.anchor;
  454. },
  455. /**
  456. * Returns the text node.
  457. *
  458. * @return {HtmlNode} The DOM text node.
  459. */
  460. getTextEl : function() {
  461. return this.textNode;
  462. },
  463. /**
  464. * Returns the icon &lt;img> element.
  465. *
  466. * @return {HtmlElement} The DOM image element.
  467. */
  468. getIconEl : function() {
  469. return this.iconNode;
  470. },
  471. /**
  472. * Returns the checked status of the node. If the node was rendered with no
  473. * checkbox, it returns false.
  474. *
  475. * @return {Boolean} The checked flag.
  476. */
  477. isChecked : function() {
  478. return this.checkbox ? this.checkbox.checked : false;
  479. },
  480. // private
  481. updateExpandIcon : function() {
  482. if (this.rendered) {
  483. var n = this.node, c1, c2;
  484. var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
  485. var hasChild = n.hasChildNodes();
  486. if (hasChild || n.attributes.expandable) {
  487. if (n.expanded) {
  488. cls += "-minus";
  489. c1 = "x-tree-node-collapsed";
  490. c2 = "x-tree-node-expanded";
  491. } else {
  492. cls += "-plus";
  493. c1 = "x-tree-node-expanded";
  494. c2 = "x-tree-node-collapsed";
  495. }
  496. if (this.wasLeaf) {
  497. this.removeClass("x-tree-node-leaf");
  498. this.wasLeaf = false;
  499. }
  500. if (this.c1 != c1 || this.c2 != c2) {
  501. Ext.fly(this.elNode).replaceClass(c1, c2);
  502. this.c1 = c1;
  503. this.c2 = c2;
  504. }
  505. } else {
  506. if (!this.wasLeaf) {
  507. Ext.fly(this.elNode).replaceClass("x-tree-node-expanded",
  508. "x-tree-node-leaf");
  509. delete this.c1;
  510. delete this.c2;
  511. this.wasLeaf = true;
  512. }
  513. }
  514. var ecc = "x-tree-ec-icon " + cls;
  515. if (this.ecc != ecc) {
  516. this.ecNode.className = ecc;
  517. this.ecc = ecc;
  518. }
  519. }
  520. },
  521. // private
  522. getChildIndent : function() {
  523. if (!this.childIndent) {
  524. var buf = [];
  525. var p = this.node;
  526. while (p) {
  527. if (!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)) {
  528. if (!p.isLast()) {
  529. buf.unshift('<img src="' + this.emptyIcon
  530. + '" class="x-tree-elbow-line" />');
  531. } else {
  532. buf.unshift('<img src="' + this.emptyIcon
  533. + '" class="x-tree-icon" />');
  534. }
  535. }
  536. p = p.parentNode;
  537. }
  538. this.childIndent = buf.join("");
  539. }
  540. return this.childIndent;
  541. },
  542. // private
  543. renderIndent : function() {
  544. if (this.rendered) {
  545. var indent = "";
  546. var p = this.node.parentNode;
  547. if (p) {
  548. indent = p.ui.getChildIndent();
  549. }
  550. if (this.indentMarkup != indent) { // don't rerender if not
  551. // required
  552. this.indentNode.innerHTML = indent;
  553. this.indentMarkup = indent;
  554. }
  555. this.updateExpandIcon();
  556. }
  557. },
  558. destroy : function() {
  559. if (this.elNode) {
  560. Ext.dd.Registry.unregister(this.elNode.id);
  561. }
  562. delete this.elNode;
  563. delete this.ctNode;
  564. delete this.indentNode;
  565. delete this.ecNode;
  566. delete this.iconNode;
  567. delete this.checkbox;
  568. delete this.anchor;
  569. delete this.textNode;
  570. Ext.removeNode(this.ctNode);
  571. }
  572. };
  573. /**
  574. * @class Ext.tree.RootTreeNodeUI This class provides the default UI
  575. * implementation for <b>root</b> Ext TreeNodes. The RootTreeNode UI
  576. * implementation allows customizing the appearance of the root tree
  577. * node.<br>
  578. * <p>
  579. * If you are customizing the Tree's user interface, you may need to
  580. * extend this class, but you should never need to instantiate this
  581. * class.<br>
  582. */
  583. Ext.tree.RootTreeNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
  584. // private
  585. render : function() {
  586. if (!this.rendered) {
  587. var targetNode = this.node.ownerTree.innerCt.dom;
  588. this.node.expanded = true;
  589. targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
  590. this.wrap = this.ctNode = targetNode.firstChild;
  591. }
  592. },
  593. collapse : Ext.emptyFn,
  594. expand : Ext.emptyFn
  595. });