PageRenderTime 29ms CodeModel.GetById 10ms app.highlight 15ms RepoModel.GetById 0ms app.codeStats 0ms

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