/javascripts/lib/src/widgets/tree/TreeNodeUI.js

https://bitbucket.org/ksokmesa/sina-asian · JavaScript · 627 lines · 457 code · 62 blank · 108 comment · 85 complexity · 15a3b67711ae2fec34c3376fea833294 MD5 · raw file

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