Advertisement
Guest User

Mif.Tree Mootool 1.2

a guest
Feb 2nd, 2012
287
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2. Mif.Tree
  3. */
  4. if(!Mif) var Mif={};
  5.  
  6. Mif.Tree = new Class({
  7.  
  8.     Implements: [new Events, new Options],
  9.        
  10.     options:{
  11.         types: {},
  12.         forest: false,
  13.         animateScroll: true,
  14.         height: 18
  15.     },
  16.    
  17.     initialize: function(options) {
  18.         this.setOptions(options);
  19.         $extend(this, {
  20.             types: this.options.types,
  21.             forest: this.options.forest,
  22.             animateScroll: this.options.animateScroll,
  23.             dfltType: this.options.dfltType,
  24.             height: this.options.height,
  25.             container: $(options.container),
  26.             UID: 0,
  27.             $: {},
  28.             key: {}
  29.         });
  30.         this.defaults={
  31.             name: '',
  32.             cls: '',
  33.             openIcon: 'mif-tree-empty-icon',
  34.             closeIcon: 'mif-tree-empty-icon',
  35.             loadable: false
  36.         };
  37.         this.dfltState={
  38.             open: false
  39.         }
  40.         Mif.Tree.UID++;
  41.         this.DOMidPrefix='mif-tree-'+Mif.Tree.UID+'-';
  42.         this.wrapper=new Element('div').addClass('mif-tree-wrapper').injectInside(this.container);
  43.         this.initEvents();
  44.         this.initScroll();
  45.         this.initSelection();
  46.         this.initHover();
  47.     },
  48.    
  49.     initEvents: function(){
  50.         this.wrapper.addEvents({
  51.             mousemove: this.mouse.bindWithEvent(this),
  52.             mouseover: this.mouse.bindWithEvent(this),
  53.             mouseout: this.mouse.bindWithEvent(this),
  54.             mouseleave: this.mouseLeave.bind(this),
  55.             mousedown: $lambda(false),
  56.             click: this.toggleClick.bindWithEvent(this),
  57.             dblclick: this.toggleDblclick.bindWithEvent(this),
  58.             keydown: this.keyDown.bindWithEvent(this),
  59.             keyup: this.keyUp.bindWithEvent(this)
  60.         });
  61.     },
  62.    
  63.     $getIndex: function(){//return array of visible nodes.
  64.         this.$index=[];
  65.         var node=this.forest ? this.root.getFirst() : this.root;
  66.         do{
  67.             this.$index.push(node);
  68.         }while(node=node.getNextVisible());
  69.     },
  70.    
  71.     mouseLeave: function(){
  72.         this.mouse.coords={x:null,y:null};
  73.         this.mouse.target=false;
  74.         this.mouse.node=false;
  75.         if(this.hover) this.hover();
  76.     },
  77.    
  78.     mouse: function(event){
  79.         this.mouse.coords=this.getCoords(event);
  80.         var target=this.getTarget(event);
  81.         this.mouse.target=target.target;
  82.         this.mouse.node = target.node;
  83.     },
  84.    
  85.     getTarget: function(event){
  86.         var target=event.target;
  87.         while(!/mif-tree/.test(target.className)){
  88.             target=target.parentNode;
  89.         }
  90.         var test=target.className.match(/mif-tree-(gadjet)-[^n]|mif-tree-(icon)|mif-tree-(name)|mif-tree-(checkbox)/);
  91.         if(!test){
  92.             var y=this.mouse.coords.y;
  93.             if(y==-1||!this.$index) {
  94.                 node=false;
  95.             }else{
  96.                 node=this.$index[((y)/this.height).toInt()];
  97.             }
  98.             return {
  99.                 node: node,
  100.                 target: 'node'
  101.             }
  102.         }
  103.         for(var i=5;i>0;i--){
  104.             if(test[i]){
  105.                 var type=test[i];
  106.                 break;
  107.             }
  108.         }
  109.         return {
  110.             node: this.$[target.getAttribute('uid')],
  111.             target: type
  112.         }
  113.     },
  114.    
  115.     getCoords: function(event){
  116.         var position=this.wrapper.getPosition();
  117.         var x=event.page.x-position.x;
  118.         var y=event.page.y-position.y;
  119.         var wrapper=this.wrapper;
  120.         if((y-wrapper.scrollTop>wrapper.clientHeight)||(x-wrapper.scrollLeft>wrapper.clientWidth)){//scroll line
  121.             y=-1;
  122.         }
  123.         return{
  124.             x: x,
  125.             y: y
  126.         };
  127.     },
  128.    
  129.     keyDown: function(event){
  130.         this.key=event;
  131.         this.key.state='down';
  132.     },
  133.    
  134.     keyUp: function(event){
  135.         this.key={};
  136.         this.key.state='up';
  137.     },
  138.    
  139.     toggleDblclick: function(event){
  140.         var target=this.mouse.target;
  141.         if(!(target=='name'||target=='icon')) return;
  142.         this.mouse.node.toggle();
  143.     },
  144.    
  145.     toggleClick: function(event){
  146.         if(this.mouse.target!='gadjet') return;
  147.         this.mouse.node.toggle();
  148.     },
  149.    
  150.     initScroll: function(){
  151.         this.scroll=new Fx.Scroll(this.wrapper);
  152.     },
  153.    
  154.     scrollTo: function(node){
  155.         var position=node.getVisiblePosition();
  156.         var top=position*this.height;
  157.         var up=top<this.wrapper.scrollTop;
  158.         var down=top>(this.wrapper.scrollTop+this.wrapper.offsetHeight);
  159.         if(position==-1 || ( !up && !down ) ) {
  160.             this.scroll.fireEvent('complete');
  161.             return false;
  162.         }
  163.         if(this.animateScroll){
  164.             this.scroll.start(this.wrapper.scrollLeft, top-(down ? this.wrapper.offsetHeight-this.height : 0));
  165.         }else{
  166.             this.scroll.set(this.wrapper.scrollLeft, top-(down ? this.wrapper.offsetHeight-this.height : 0));
  167.             this.scroll.fireEvent('complete');
  168.         }
  169.     },
  170.    
  171.     serialize: function(items){
  172.         var serial = [];
  173.         if (!items) items = this.root.getChildren();
  174.         items.each(function(el, i){            
  175.         serial[i] = {
  176.                 id: el.id,
  177.                 children: (el.getChildren()) ? tree.serialize(el.getChildren()) : []
  178.             };
  179.         });
  180.         return serial;
  181.     }
  182.    
  183. });
  184. Mif.Tree.UID=0;
  185.  
  186.  
  187. /*
  188. Mif.Tree.Node
  189. */
  190. Mif.Tree.Node = new Class({
  191.  
  192.     Implements: [new Events],
  193.    
  194.     initialize: function(structure, options) {
  195.         $extend(this, structure);
  196.         this.children=[];
  197.         this.type=options.type||this.tree.dfltType;
  198.         this.property=options.property;
  199.         this.data=options.data;
  200.         this.state=$unlink($extend(this.tree.dfltState, options.state));
  201.         this.$calculate();
  202.        
  203.         this.UID=this.tree.UID++;
  204.         this.tree.$[this.UID]=this;
  205.     },
  206.    
  207.     $calculate: function(){
  208.         $extend(this, this.tree.defaults);
  209.         this.type=$splat(this.type);
  210.         this.type.each(function(type){
  211.             var props=this.tree.types[type];
  212.             if(props) $extend(this, props);
  213.         }, this);
  214.         $extend(this, this.property);
  215.     },
  216.    
  217.     getDOM: function(what){
  218.         var node=$(this.tree.DOMidPrefix+this.UID);
  219.         if(what=='node') return node;
  220.         var wrapper=node.getFirst();
  221.         if(what=='wrapper') return wrapper;
  222.         if(what=='children') return wrapper.getNext();
  223.         return wrapper.getElement('.mif-tree-'+what);
  224.     },
  225.    
  226.     getGadjetType: function(){
  227.         return (this.loadable && !this.isLoaded()) ? 'plus' : (this.hasChildren() ? (this.isOpen() ? 'minus' : 'plus') : 'none');
  228.     },
  229.    
  230.     toggle: function(state) {
  231.         if(this.state.open==state || this.$loading || this.$toggling) return;
  232.         if(this.loadable && !this.state.loaded) {
  233.             this.addEvent('load',function(){
  234.                 this.toggle();
  235.             }.bind(this));
  236.             this.load();
  237.             return;
  238.         }
  239.         if(!this.hasChildren()) return;
  240.         var next=this.getNextVisible();
  241.         this.state.open = !this.state.open;
  242.         var state=this.state.open;
  243.         if(!this.$draw) Mif.Tree.Draw.children(this);
  244.         var children=this.getDOM('children');  
  245.         var gadjet=this.getDOM('gadjet');
  246.         var icon=this.getDOM('icon');
  247.         children.style.display=this.isOpen() ? 'block' : 'none';
  248.         gadjet.className='mif-tree-gadjet mif-tree-gadjet-'+this.getGadjetType();
  249.         icon.className='mif-tree-icon '+this[this.isOpen() ? 'openIcon' : 'closeIcon'];
  250.         this.tree.hoverState.gadjet=false;
  251.         this.tree.hover();
  252.         this.tree.$getIndex();
  253.         this.tree.fireEvent('toggle', [this, this.state.open]);
  254.     },
  255.    
  256.     recursive: function(fn, args){
  257.         args=$splat(args);
  258.         if(fn.apply(this, args)!==false){
  259.             this.children.each(function(node){
  260.                 node.recursive(fn, args);
  261.             });
  262.         }
  263.         return this;
  264.     },
  265.    
  266.     isOpen: function(){
  267.         return this.state.open;
  268.     },
  269.    
  270.     isLoaded: function(){
  271.         return this.state.loaded;
  272.     },
  273.    
  274.     isLast: function(){
  275.         if(this.parentNode==null || this.parentNode.children.getLast()==this) return true;
  276.         return false;
  277.     },
  278.    
  279.     isFirst: function(){
  280.         if(this.parentNode==null || this.parentNode.children[0]==this) return true;
  281.         return false;
  282.     },
  283.    
  284.     isRoot: function(){
  285.         return this.parentNode==null ? true : false;
  286.     },
  287.    
  288.     getChildren: function(){
  289.         return this.children;
  290.     },
  291.    
  292.     hasChildren: function(){
  293.         return this.children.length ? true : false;
  294.     },
  295.    
  296.     index: function(){
  297.         if( this.isRoot() ) return 0;
  298.         return this.parentNode.children.indexOf(this);
  299.     },
  300.    
  301.     getNext: function(){
  302.         if(this.isLast()) return null;
  303.         return this.parentNode.children[this.index()+1];
  304.     },
  305.    
  306.     getPrevious: function(){
  307.         if( this.isFirst() ) return null;
  308.         return this.parentNode.children[this.index()-1];
  309.     },
  310.    
  311.     getFirst: function(){
  312.         if(!this.hasChildren()) return null;
  313.         return this.children[0];
  314.     },
  315.    
  316.     getLast: function(){
  317.         if(!this.hasChildren()) return null;
  318.         return this.children.getLast();    
  319.     },
  320.    
  321.     getParent: function(){
  322.         return this.parentNode;
  323.     },
  324.    
  325.     getNextVisible: function(){
  326.         var current=this;
  327.         if(current.isRoot()){
  328.             if(!current.isOpen() || !current.hasChildren()) return false;
  329.             return current.getFirst();
  330.         }else{
  331.             if(current.isOpen() && current.getFirst()){
  332.                 return current.getFirst();
  333.             }else{
  334.                 var parent=current;
  335.                 do{
  336.                     current=parent.getNext();
  337.                     if(current) return current;
  338.                 }while( parent=parent.parentNode )
  339.                 return false;
  340.             }
  341.         }
  342.     },
  343.    
  344.     getPreviousVisible: function(){
  345.         var current=this;
  346.         if( current.isFirst() && ( !current.parentNode || (current.tree.forest && current.parentNode.isRoot()) ) ){
  347.             return false;
  348.         }else{
  349.             if( current.getPrevious() ){
  350.                 current=current.getPrevious();
  351.                 while( current.isOpen() && current.getLast() ){
  352.                     current=current.getLast();
  353.                 }
  354.                 return current;
  355.             }else{
  356.                 return current.parentNode;
  357.             }
  358.         }
  359.     },
  360.    
  361.     getVisiblePosition: function(){
  362.         return this.tree.$index.indexOf(this);
  363.     },
  364.        
  365.     contains: function(node){
  366.         do{
  367.             if(node==this) return true;
  368.             node=node.parentNode;
  369.         }while(node);
  370.         return false;
  371.     },
  372.  
  373.     addType: function(type){
  374.         this.type.include(type);
  375.         this.$calculate();
  376.         Mif.Tree.Draw.update(this);
  377.         return this;
  378.     },
  379.  
  380.     removeType: function(type){
  381.         this.type.erase(type);
  382.         this.$calculate();
  383.         Mif.Tree.Draw.update(this);
  384.         return this;
  385.     },
  386.    
  387.     set: function(props){
  388.         this.tree.fireEvent('beforeSet', [this]);
  389.         $extend(this, props);
  390.         if(props.property||props.type||props.state){
  391.             this.$calculate();
  392.             Mif.Tree.Draw.update(this);
  393.         }
  394.         this.tree.fireEvent('set', [this, props]);
  395.     }
  396.    
  397. });
  398.  
  399.  
  400. /*
  401. Mif.Tree.Draw
  402. */
  403. Mif.Tree.Draw={
  404.  
  405.     getHTML: function(node,html){
  406.         var prefix=node.tree.DOMidPrefix;
  407.         if($defined(node.state.checked)){
  408.             var checkbox='<span class="mif-tree-checkbox mif-tree-node-'+node.state.checked+'" uid="'+node.UID+'">'+Mif.Tree.Draw.zeroSpace+'</span>';
  409.         }else{
  410.             var checkbox='';
  411.         }
  412.         html=html||[];
  413.         html.push(
  414.         '<div class="mif-tree-node ',(node.isLast() ? 'mif-tree-node-last' : ''),'" id="',prefix,node.UID,'">',
  415.             '<span class="mif-tree-node-wrapper ',node.cls,'" uid="',node.UID,'">',
  416.                 '<span class="mif-tree-gadjet mif-tree-gadjet-',node.getGadjetType(),'" uid="',node.UID,'">',Mif.Tree.Draw.zeroSpace,'</span>',
  417.                 checkbox,
  418.                 '<span class="mif-tree-icon ',node.closeIcon,'" uid="',node.UID,'">',Mif.Tree.Draw.zeroSpace,'</span>',
  419.                 '<span class="mif-tree-name" uid="',node.UID,'">',node.name,'</span>',
  420.             '</span>',
  421.             '<div class="mif-tree-children" style="display:none"></div>',
  422.         '</div>'
  423.         );
  424.         return html;
  425.     },
  426.    
  427.     children: function(parent, container){
  428.         parent.open=true;
  429.         parent.$draw=true;
  430.         var html=[];
  431.         var children=parent.children;
  432.         for(var i=0,l=children.length;i<l;i++){
  433.             this.getHTML(children[i],html);
  434.         }
  435.         container=container || parent.getDOM('children');
  436.         container.set('html', html.join(''));
  437.         parent.tree.fireEvent('drawChildren',[parent]);
  438.     },
  439.    
  440.     root: function(tree){
  441.         var domRoot=this.node(tree.root);
  442.         domRoot.injectInside(tree.wrapper);
  443.         tree.fireEvent('drawRoot');
  444.     },
  445.    
  446.     forestRoot: function(tree){
  447.         var container=new Element('div').addClass('mif-tree-children-root').injectInside(tree.wrapper);
  448.         Mif.Tree.Draw.children(tree.root, container);
  449.     },
  450.    
  451.     node: function(node){
  452.         return new Element('div').set('html', this.getHTML(node).join('')).getFirst();
  453.     },
  454.    
  455.     update: function(node){
  456.         if(!node) return;
  457.         if(node.tree.forest && node.isRoot()) return;
  458.         if(!node.hasChildren()) node.state.open=false;
  459.         node.getDOM('name').set('html', node.name);
  460.         node.getDOM('gadjet').className='mif-tree-gadjet mif-tree-gadjet-'+node.getGadjetType();
  461.         node.getDOM('icon').className='mif-tree-icon '+node[node.isOpen() ? 'openIcon' : 'closeIcon'];
  462.         node.getDOM('node')[(node.isLast() ?'add' : 'remove')+'Class']('mif-tree-node-last');
  463.         if(node.$loading) return;
  464.         var children=node.getDOM('children');
  465.         children.className='mif-tree-children';
  466.         if(node.isOpen()){
  467.             if(!node.$draw) Mif.Tree.Draw.children(node);
  468.             children.style.display='block';
  469.         }else{
  470.             children.style.display='none';
  471.         }
  472.         node.tree.fireEvent('updateNode', node);
  473.         return node;
  474.     },
  475.    
  476.     updateDOM: function(node, domNode){
  477.         domNode= domNode||node.getDOM('node');
  478.         var previous=node.getPrevious();
  479.         if(previous){
  480.             domNode.injectAfter(previous.getDOM('node'));
  481.         }else{
  482.             domNode.injectTop(node.parentNode.getDOM('children'));
  483.         }
  484.     }
  485.    
  486. };
  487. Mif.Tree.Draw.zeroSpace=Browser.Engine.trident ? '&shy;' : (Browser.Engine.webkit ? '&#8203' : '');
  488.  
  489.  
  490. /*
  491. Mif.Tree.Selection
  492. */
  493. Mif.Tree.implement({
  494.    
  495.     initSelection: function(){
  496.         this.defaults.selectClass='';
  497.         this.wrapper.addEvent('mousedown', this.attachSelect.bindWithEvent(this));
  498.     },
  499.    
  500.     attachSelect: function(event){
  501.         if(!['icon', 'name', 'node'].contains(this.mouse.target)) return;
  502.         var node=this.mouse.node;
  503.         if(!node) return;
  504.         this.select(node);
  505.     },
  506.    
  507.     select: function(node) {
  508.         if(Browser.Engine.gecko) {
  509.             this.wrapper.focus();
  510.         }
  511.         var current=this.selected;
  512.         if (current==node) return;
  513.         if (current) {
  514.             current.select(false);
  515.         }
  516.         this.selected = node;
  517.         node.select(true);
  518.     },
  519.    
  520.     unselect: function(){
  521.         var current=this.selected;
  522.         if(!current) return;
  523.         this.selected=false;
  524.         current.select(false);
  525.     },
  526.    
  527.     getSelected: function(){
  528.         return this.selected;
  529.     },
  530.    
  531.     isSelected: function(node){
  532.         return node.isSelected();
  533.     }
  534.    
  535. });
  536.  
  537. Mif.Tree.Node.implement({
  538.        
  539.     select: function(state) {
  540.         this.state.selected = state;
  541.         var wrapper=this.getDOM('wrapper');
  542.         wrapper[(state ? 'add' : 'remove')+'Class'](this.selectClass||'mif-tree-node-selected');
  543.         this.tree.fireEvent(state ? 'select' : 'unSelect', [this]);
  544.         this.tree.fireEvent('selectChange', [this, state]);
  545.     },
  546.    
  547.     isSelected: function(){
  548.         return this.state.selected;
  549.     }
  550.    
  551. });
  552.  
  553.  
  554. /*
  555. Mif.Tree.Hover
  556. */
  557. Mif.Tree.implement({
  558.    
  559.     initHover: function(){
  560.         this.defaults.hoverClass='';
  561.         this.wrapper.addEvent('mousemove', this.hover.bind(this));
  562.         this.wrapper.addEvent('mouseout', this.hover.bind(this));
  563.         this.defaultHoverState={
  564.             gadjet: false,
  565.             checkbox: false,
  566.             icon: false,
  567.             name: false,
  568.             node: false
  569.         }
  570.         this.hoverState=$unlink(this.defaultHoverState);
  571.     },
  572.    
  573.     hover: function(){
  574.         var cnode=this.mouse.node;
  575.         var ctarget=this.mouse.target;
  576.         $each(this.hoverState, function(node, target, state){
  577.             if(node==cnode && (target=='node'||target==ctarget)) return;
  578.             if(node) {
  579.                 Mif.Tree.Hover.out(node, target);
  580.                 this.fireEvent('hover', [node, target, 'out']);
  581.                 state[target]=false;
  582.             }
  583.             if(cnode && (target=='node'||target==ctarget)) {
  584.                 Mif.Tree.Hover.over(cnode, target);
  585.                 this.fireEvent('hover', [cnode, target, 'over']);
  586.                 state[target]=cnode;
  587.             }else{
  588.                 state[target]=false;
  589.             }
  590.         }, this);
  591.     },
  592.    
  593.     updateHover: function(){
  594.         this.hoverState=$unlink(this.defaultHoverState);
  595.         this.hover();
  596.     }
  597.    
  598. });
  599.  
  600. Mif.Tree.Hover={
  601.    
  602.     over: function(node, target){
  603.         var wrapper=node.getDOM('wrapper');
  604.         wrapper.addClass((node.hoverClass||'mif-tree-hover')+'-'+target);
  605.         if(node.state.selected) wrapper.addClass((node.hoverClass||'mif-tree-hover')+'-selected-'+target);
  606.     },
  607.    
  608.     out: function(node, target){
  609.         var wrapper=node.getDOM('wrapper');
  610.         wrapper.removeClass((node.hoverClass||'mif-tree-hover')+'-'+target).removeClass((node.hoverClass||'mif-tree-hover')+'-selected-'+target);
  611.     }
  612.    
  613. }
  614.  
  615.  
  616. /*
  617. Mif.Tree.Load
  618. */
  619. Mif.Tree.Load={
  620.        
  621.     children: function(children, parent, tree){
  622.         for( var i=children.length; i--; ){
  623.             var child=children[i];
  624.             var subChildren=child.children;
  625.             delete child.children;
  626.             var node=new Mif.Tree.Node({
  627.                 tree: tree,
  628.                 parentNode: parent||undefined
  629.             }, child);
  630.             if( tree.forest || parent != undefined){
  631.                 parent.children.unshift(node);
  632.             }else{
  633.                 tree.root=node;
  634.             }
  635.             if(subChildren && subChildren.length){
  636.                 arguments.callee(subChildren, node, tree);
  637.             }
  638.         }
  639.         if(parent) parent.state.loaded=true;
  640.         tree.fireEvent('loadChildren', parent);
  641.     }
  642.    
  643. };
  644.  
  645. Mif.Tree.implement({
  646.  
  647.     load: function(options){
  648.         var tree=this;
  649.         this.loadOptions=this.loadOptions||$lambda({});
  650.         function success(json){
  651.             if(tree.forest){
  652.                 tree.root=new Mif.Tree.Node({
  653.                     tree: tree,
  654.                     parentNode: null
  655.                 }, {});
  656.                 var parent=tree.root;
  657.             }else{
  658.                 var parent=null;
  659.             }
  660.             Mif.Tree.Load.children(json, parent, tree);
  661.             Mif.Tree.Draw[tree.forest ? 'forestRoot' : 'root'](tree);
  662.             tree.$getIndex();
  663.             tree.fireEvent('load');
  664.             return tree;
  665.         }
  666.         options=$extend($extend({
  667.             isSuccess: $lambda(true),
  668.             secure: true,
  669.             onSuccess: success,
  670.             method: 'get'
  671.         }, this.loadOptions()), options);
  672.         if(options.json) return success(options.json);
  673.         new Request.JSON(options).send();
  674.         return this;
  675.     },
  676.     del: function(){
  677.         this.UID=0;
  678.         this.$={};
  679.         this.$index=null;
  680.         this.mouse.node=false;
  681.         this.unselect();
  682.         this.wrapper.innerHTML='';
  683.     }   ,
  684.     reload: function(options){
  685.         var tree=this;
  686.         this.del();
  687.         this.load(options);
  688.     }  
  689. });
  690.  
  691. Mif.Tree.Node.implement({
  692.    
  693.     load: function(options){
  694.         this.$loading=true;
  695.         options=options||{};
  696.         this.addType('loader');
  697.         var self=this;
  698.         function success(json){
  699.             Mif.Tree.Load.children(json, self, self.tree);
  700.             delete self.$loading;
  701.             self.state.loaded=true;
  702.             self.removeType('loader');
  703.             self.fireEvent('load');
  704.             return self;
  705.         }
  706.         options=$extend($extend($extend({
  707.             isSuccess: $lambda(true),
  708.             secure: true,
  709.             onSuccess: success,
  710.             method: 'get'
  711.         }, this.tree.loadOptions(this)), this.loadOptions), options);
  712.         if(options.json) return success(options.json);
  713.         new Request.JSON(options).send();
  714.         return this;
  715.     }
  716.    
  717. });
  718.  
  719.  
  720. /*
  721. Mif.Tree.KeyNav
  722. */
  723. Mif.Tree.KeyNav=new Class({
  724.    
  725.     initialize: function(tree, options){
  726.         this.tree=tree;
  727.         tree.wrapper.addEvent('keydown',function(event){
  728.             if(!['down','left','right','up'].contains(event.key)) return;
  729.             if(!tree.selected){
  730.                 tree.select(tree.forest ? tree.root.getFirst() : tree.root);
  731.             }else{
  732.                 var current=tree.selected;
  733.                 switch (event.key){
  734.                     case 'down': this.goForward(current);event.stop();break;  
  735.                     case 'up': this.goBack(current);event.stop();break;  
  736.                     case 'left': this.goLeft(current);event.stop();break;
  737.                     case 'right': this.goRight(current);event.stop();break;
  738.                 }
  739.             }
  740.             var height=tree.height;
  741.             function autoScroll(){
  742.                 var wrapper=tree.wrapper;
  743.                 var i=tree.selected.getVisiblePosition();
  744.                 var top=i*height-wrapper.scrollTop;
  745.                 var bottom=top+height;
  746.                 if(top<height){
  747.                     wrapper.scrollTop-=height;
  748.                 }
  749.                 if(wrapper.offsetHeight-bottom<height){
  750.                     wrapper.scrollTop+=height;
  751.                 }
  752.             }
  753.             autoScroll();
  754.         }.bind(this));
  755.     },
  756.  
  757.     goForward: function(current){
  758.         var forward=current.getNextVisible();
  759.         if( forward ) this.tree.select(forward)
  760.     },
  761.    
  762.     goBack: function(current){
  763.         var back=current.getPreviousVisible();
  764.         if (back) this.tree.select(back);
  765.     },
  766.    
  767.     goLeft: function(current){
  768.         if(current.isRoot()){
  769.             if(current.isOpen()){
  770.                 current.toggle();
  771.             }else{
  772.                 return false;
  773.             }
  774.         }else{
  775.             if( current.hasChildren() && current.isOpen() ){
  776.                 current.toggle();
  777.             }else{
  778.                 if(current.tree.forest && current.getParent().isRoot()) return false;
  779.                 return this.tree.select(current.getParent());
  780.             }
  781.         }
  782.     },
  783.    
  784.     goRight: function(current){
  785.         if(!current.hasChildren()&&!current.loadable){
  786.             return false;
  787.         }else if(!current.isOpen()){
  788.             return current.toggle();
  789.         }else{
  790.             return this.tree.select(current.getFirst());
  791.         }
  792.     }
  793. });
  794.  
  795.  
  796. /*
  797. Mif.Tree.Sort
  798. */
  799. Mif.Tree.implement({
  800.    
  801.     initSortable: function(sortFunction){
  802.         this.sortable=true;
  803.         this.sortFunction=sortFunction||function(node1, node2){
  804.             if(node1.name>node2.name){
  805.                 return 1;
  806.             }else if(node1.name<node2.name){
  807.                 return -1;
  808.             }else{
  809.                 return 0;
  810.             }
  811.         }
  812.         this.addEvent('loadChildren', function(parent){
  813.             if(parent) parent.sort();
  814.         });
  815.         this.addEvent('structureChange', function(from, to, where, type){
  816.             from.sort();
  817.         });
  818.         return this;
  819.     }
  820.    
  821. });
  822.  
  823.  
  824. Mif.Tree.Node.implement({
  825.  
  826.     sort: function(sortFunction){
  827.         this.children.sort(sortFunction||this.tree.sortFunction);
  828.         return this;
  829.     }
  830.    
  831. });
  832.  
  833.  
  834. /*
  835. Mif.Tree.Transform
  836. */
  837. Mif.Tree.Node.implement({
  838.    
  839.     inject: function(node, where, domNode){//domNode - internal property
  840.         var parent=this.parentNode;
  841.         var previous=this.getPrevious();
  842.         var type=domNode ? 'copy' : 'move';
  843.         switch(where){
  844.             case 'after':
  845.             case 'before':
  846.                 if( node['get'+(where=='after' ? 'Next' : 'Previous')]()==this ) return false;
  847.                 if(this.parentNode) this.parentNode.children.erase(this);
  848.                 this.parentNode=node.parentNode;
  849.                 this.parentNode.children.inject(this, node, where);
  850.                 break;
  851.             case 'inside':
  852.                 if( node.getLast()==this ) return false;
  853.                 if(this.parentNode) this.parentNode.children.erase(this);
  854.                 node.children.push(this);
  855.                 this.parentNode=node;
  856.                 node.$draw=true;
  857.                 node.state.open=true;
  858.                 break;
  859.         }      
  860.         this.tree.fireEvent('structureChange', [this, node, where, type]);
  861.         Mif.Tree.Draw.updateDOM(this, domNode);
  862.         [node, this, parent, previous, this.getPrevious()].each(function(node){
  863.             Mif.Tree.Draw.update(node);
  864.         });
  865.         this.tree.select(this);
  866.         this.tree.$getIndex();
  867.         this.tree.scrollTo(this);
  868.         return this;
  869.     },
  870.    
  871.     copy: function(node, where){
  872.         function copy(structure){
  873.             var node=structure.node;
  874.             var tree=structure.tree;
  875.             var options=$unlink({
  876.                 property: node.property,
  877.                 type: node.type,
  878.                 state: node.state,
  879.                 data: node.data
  880.             });
  881.             options.state.open=false;
  882.             var nodeCopy = new Mif.Tree.Node({
  883.                 parent: structure.parentNode,
  884.                 children: [],
  885.                 tree: tree
  886.             }, options);
  887.             node.children.each(function(child){
  888.                 var childCopy=copy({
  889.                     node: child,
  890.                     parent: nodeCopy,
  891.                     tree: tree
  892.                 })
  893.                 nodeCopy.children.push(childCopy);
  894.             });
  895.             return nodeCopy;
  896.         }
  897.         var nodeCopy=copy({
  898.             node: this,
  899.             parent: null,
  900.             tree: this.tree
  901.         });
  902.         return nodeCopy.inject(node, where, Mif.Tree.Draw.node(nodeCopy));
  903.     },
  904.    
  905.     remove: function(){
  906.         this.tree.fireEvent('remove', [this]);
  907.         var parent=this.parentNode, previous=this.getPrevious();
  908.         if(parent) parent.children.erase(this);
  909.         this.tree.selected=false;
  910.         this.getDOM('node').destroy();
  911.         Mif.Tree.Draw.update(parent);
  912.         Mif.Tree.Draw.update(previous);
  913.         this.tree.mouse.node=false;
  914.         this.tree.updateHover();
  915.         this.tree.$getIndex();
  916.     }
  917.    
  918. });
  919.  
  920.  
  921. Mif.Tree.implement({
  922.  
  923.     move: function(from, to, where){
  924.         if ( from.inject(to, where) ){
  925.             this.fireEvent('move', [from, to, where]);
  926.         }
  927.         return this;
  928.     },
  929.    
  930.     copy: function(from, to, where){
  931.         var copy = from.copy(to, where);
  932.         if ( copy ){
  933.             this.fireEvent('copy', [from, to, where, copy]);
  934.         }
  935.         return this;
  936.     },
  937.    
  938.     remove: function(node){
  939.         node.remove();
  940.         return this;
  941.     },
  942.    
  943.     add: function(node){
  944.        
  945.     }
  946.    
  947. });
  948.  
  949. Array.implement({
  950.    
  951.     inject: function(added, current, where){//inject added after or before current;
  952.         var pos=this.indexOf(current)+(where=='before' ? 0 : 1);
  953.         for(var i=this.length-1;i>=pos;i--){
  954.             this[i+1]=this[i];
  955.         }
  956.         this[pos]=added;
  957.         return this;
  958.     }
  959.    
  960. });
  961.  
  962. /*
  963. Mif.Tree.Drag
  964. */
  965. Mif.Tree.Drag = new Class({
  966.    
  967.     Implements: [new Events, new Options],
  968.    
  969.     options:{
  970.         snap: 4,
  971.         animate: true,
  972.         open: 600,//time to open node
  973.         scrollDelay: 100,
  974.         scrollSpeed: 100,
  975.         modifier: 'control',//copy
  976.         startPlace: ['icon', 'name']
  977.     },
  978.  
  979.     initialize: function(tree, options){
  980.         tree.drag=this;
  981.         this.setOptions(options);
  982.         $extend(this,{
  983.             tree: tree,
  984.             dragged: false,
  985.             snap: this.options.snap
  986.         });
  987.         $extend(tree.defaults, {
  988.             dropDenied: [],
  989.             dragDisabled: false
  990.         });
  991.         tree.addEvent('drawRoot',function(){
  992.             tree.root.dropDenied.include('before').include('after');
  993.         });
  994.         this.pointer=new Element('div').addClass('mif-tree-pointer').injectInside(tree.wrapper);
  995.         this.dragTargetSelect();
  996.         this.attachMouseEvents();
  997.         this.attachEvents();
  998.     },
  999.    
  1000.     dragTargetSelect: function(){
  1001.         function addDragTarget(){
  1002.             this.current.getDOM('name').addClass('mif-tree-drag-current')
  1003.         }
  1004.         function removeDragTarget(){
  1005.             this.current.getDOM('name').removeClass('mif-tree-drag-current');
  1006.         }
  1007.         this.addEvent('start',addDragTarget.bind(this));
  1008.         this.addEvent('beforeComplete',removeDragTarget.bind(this));
  1009.     },
  1010.  
  1011.     attachMouseEvents: function(){
  1012.         this.tree.wrapper.addEvents({
  1013.             mousedown: this.mousedown.bindWithEvent(this),
  1014.             mouseup: this.mouseup.bind(this),
  1015.             mousemove: this.mousemove.bindWithEvent(this),
  1016.             keydown: this.keydown.bindWithEvent(this),
  1017.             mouseleave: this.mouseleave.bind(this)
  1018.         });
  1019.         document.addEvent('mouseup',this.externalUp.bind(this));
  1020.         this.pointer.addEvents({
  1021.             'mouseup' : this.mouseup.bind(this),
  1022.             'mousemove' : this.mousemove.bindWithEvent(this)
  1023.         });
  1024.     },
  1025.  
  1026.     mousedown: function(event){
  1027.         if(this.current || this.$completing) return;
  1028.         var target=this.tree.mouse.target;
  1029.         if(!target) return;
  1030.         this.current=$splat(this.options.startPlace).contains(target) ? this.tree.mouse.node : false;
  1031.         if(!this.current) return;
  1032.         if(this.current.DDdisabled){
  1033.             this.current=false;
  1034.         }
  1035.         if(this.current){
  1036.             this.startX = event.client.x;
  1037.             this.startY = event.client.y;
  1038.         }
  1039.     },
  1040.    
  1041.     mousemove: function(event){
  1042.         if(!this.current || this.$completing ||
  1043.             (!this.dragged && Math.sqrt(Math.pow(event.client.x-this.startX,2)+Math.pow(event.client.y-this.startY,2))<this.snap)
  1044.         ) return false;
  1045.         if(!this.dragged){
  1046.             this.start(event);
  1047.         }else{
  1048.             this.check(event);
  1049.             this.autoScroll(event);
  1050.         }
  1051.     },
  1052.  
  1053.     mouseup: function(){
  1054.         if(this.$completing) return;
  1055.         if (this.dragged) {
  1056.             this.clean();
  1057.             $clear(this.scrolling);
  1058.             this.$completing=true;
  1059.             this.fireEvent('beforeComplete');
  1060.             this.drop();
  1061.         }else if(this.current){
  1062.             this.current=false;
  1063.         }
  1064.        
  1065.     },
  1066.    
  1067.     externalUp: function(){
  1068.         if(this.dragged){
  1069.             this.fireEvent('cancel');
  1070.         }
  1071.     },
  1072.    
  1073.     mouseleave: function(){
  1074.         if(this.dragged) {
  1075.             this.where='notAllowed';
  1076.             this.fireEvent('drag');
  1077.         }
  1078.     },
  1079.    
  1080.     keydown: function(event){
  1081.         if(event.key=='esc'){
  1082.             this.fireEvent('cancel');
  1083.         }
  1084.     },
  1085.    
  1086.     autoScroll: function(event){
  1087.         var y=this.y;
  1088.         if(y==-1) return;
  1089.         var wrapper=this.tree.wrapper;
  1090.         var top=y-wrapper.scrollTop;
  1091.         var bottom=wrapper.offsetHeight-top;
  1092.         var sign=0;
  1093.         if(top<this.tree.height){
  1094.             var delta=top;
  1095.             sign=1;
  1096.         }else if(bottom<this.tree.height){
  1097.             var delta=bottom;
  1098.             sign=-1;
  1099.         }
  1100.         if(sign && !this.scrolling){
  1101.             this.scrolling=function(node){
  1102.                 if(y!=this.y){
  1103.                     y=this.y;
  1104.                     delta = (sign==1 ? (y-wrapper.scrollTop) : (wrapper.offsetHeight-y+wrapper.scrollTop))||1;
  1105.                 }
  1106.                 wrapper.scrollTop=wrapper.scrollTop-sign*this.options.scrollSpeed/delta;
  1107.             }.periodical(this.options.scrollDelay, this, [sign])
  1108.         }
  1109.         if(!sign){
  1110.             $clear(this.scrolling);
  1111.             this.scrolling=null;
  1112.         }
  1113.     },
  1114.    
  1115.     attachEvents: function(){
  1116.         this.addEvent('drag', this.drag.bind(this));
  1117.         this.addEvent('complete', this.complete.bind(this));
  1118.         this.addEvent('cancel', this.cancel.bind(this));
  1119.     },
  1120.    
  1121.     start: function(event){
  1122.         this.tree.unselect();
  1123.         this.addGhost(event);
  1124.         this.dragged=true;
  1125.         this.fireEvent('start');
  1126.     },
  1127.    
  1128.     cancel: function(){
  1129.         this.where='notAllowed';
  1130.         this.mouseup();
  1131.     },
  1132.    
  1133.     complete: function(){
  1134.         this.target=false;
  1135.         this.current=false;
  1136.         this.where=false;
  1137.         this.dragged=false;
  1138.         this.$completing=false;
  1139.         this.ghost.dispose();
  1140.     },
  1141.  
  1142.     drag: function(){
  1143.         this.clean();
  1144.         var where=this.where;
  1145.         var target=this.target;
  1146.         var ghostType=where;
  1147.         if(where=='after'&&(target.getNext())||where=='before'&&(target.getPrevious())){
  1148.             ghostType='between';
  1149.         }
  1150.         this.ghost.firstChild.className='mif-tree-ghost-icon mif-tree-ghost-'+ghostType;
  1151.         if(where == 'notAllowed'){
  1152.             this.tree.unselect();
  1153.             return;
  1154.         }
  1155.         this.tree.select(target);
  1156.         if(where == 'inside'){
  1157.             if(!target.isOpen() && !this.openTimer && (target.loadable||target.hasChildren()) ){
  1158.                 this.wrapper=target.getDOM('wrapper');
  1159.                 this.wrapper.style.cursor='progress';
  1160.                 this.openTimer=function(){
  1161.                     target.toggle();
  1162.                     this.clean();
  1163.                 }.delay(this.options.open,this);
  1164.             }
  1165.         }else{
  1166.             var wrapper=this.tree.wrapper;
  1167.             var top=this.index*this.tree.height;
  1168.             if(where=='after') top+=this.tree.height;
  1169.             this.pointer.setStyles({
  1170.                 display: 'block',
  1171.                 left: wrapper.scrollLeft,
  1172.                 top: top,
  1173.                 width: wrapper.clientWidth
  1174.             });
  1175.         }
  1176.     },
  1177.  
  1178.     clean: function(){
  1179.         this.pointer.style.width=0;
  1180.         if(this.openTimer){
  1181.             $clear(this.openTimer);
  1182.             this.openTimer=false;
  1183.             this.wrapper.style.cursor='inherit';
  1184.             this.wrapper=false;
  1185.         }
  1186.     },
  1187.    
  1188.     addGhost: function(event){
  1189.         var wrapper=this.current.getDOM('wrapper');
  1190.         var ghost=new Element('span').addClass('mif-tree-ghost');
  1191.         ghost.adopt(Mif.Tree.Draw.node(this.current).getFirst());
  1192.         ghost.injectInside(document.body)
  1193.             .addClass('mif-tree-ghost-notAllowed')
  1194.             .setStyles({
  1195.                 position:'absolute',
  1196.                 left:event.page.x+20,
  1197.                 top:event.page.y+20
  1198.             })
  1199.             .makeDraggable().start(event);
  1200.         new Element('span').set('html',Mif.Tree.Draw.zeroSpace).injectTop(ghost);
  1201.         ghost.getLast().getFirst().className='';
  1202.         this.ghost=ghost;
  1203.     },
  1204.    
  1205.     check: function(event){
  1206.         this.y=this.tree.mouse.coords.y;
  1207.         var target=this.tree.mouse.node;
  1208.         this.target=target;
  1209.         if(!target){
  1210.             this.target=false;
  1211.             this.where='notAllowed';
  1212.             this.fireEvent('drag');
  1213.             return;
  1214.         }
  1215.         if(this.current.contains(target)){
  1216.             this.where='notAllowed';
  1217.             this.fireEvent('drag');
  1218.             return;
  1219.         }
  1220.         this.index=Math.floor(this.y/this.tree.height)
  1221.         var delta=this.y-this.index*this.tree.height;
  1222.         var deny=this.target.dropDenied;
  1223.         if(this.tree.sortable){
  1224.             deny.include('before').include('after');
  1225.         }
  1226.         var where;
  1227.         if(!deny.contains('inside') && delta>(this.tree.height/4) && delta<(3/4*this.tree.height)){
  1228.             where='inside';
  1229.         }else{
  1230.             if(delta<this.tree.height/2){
  1231.                 if(deny.contains('before')){
  1232.                     if(deny.contains('inside')){
  1233.                         where=deny.contains('after') ? 'notAllowed' : 'after';
  1234.                     }else{
  1235.                         where='inside';
  1236.                     }
  1237.                 }else{
  1238.                     where='before';
  1239.                 }
  1240.             }else{
  1241.                 if(deny.contains('after')){
  1242.                     if(deny.contains('inside')){
  1243.                         where=deny.contains('before') ? 'notAllowed' : 'before';
  1244.                     }else{
  1245.                         where='inside';
  1246.                     }
  1247.                 }else{
  1248.                     where='after';
  1249.                 }
  1250.             }
  1251.         }
  1252.         if(this.where==where && this.target==target) return;
  1253.         this.where=where; this.target=target;
  1254.         this.fireEvent('drag');
  1255.     },
  1256.    
  1257.     drop: function(){
  1258.         var current=this.current, target=this.target, where=this.where;
  1259.         if(this.where=='notAllowed'){
  1260.             var scroll=this.tree.scroll;
  1261.             scroll.addEvent('complete',function(){
  1262.                 scroll.removeEvent('complete', arguments.callee);
  1263.                 if(this.options.animate){
  1264.                     var wrapper=current.getDOM('wrapper');
  1265.                     var position=wrapper.getPosition();
  1266.                     this.ghost.set('morph',{
  1267.                         duration: 'short',
  1268.                         onComplete: function(){
  1269.                             this.fireEvent('complete', [current, target, where]);
  1270.                         }.bind(this)
  1271.                     });
  1272.                     this.ghost.morph({left: position.x, top: position.y});
  1273.                     return;
  1274.                 };
  1275.                 this.fireEvent('complete', [current, target, where]);
  1276.                 return;
  1277.             }.bind(this));
  1278.             this.tree.select(this.current);
  1279.             this.tree.scrollTo(this.current);
  1280.             return;
  1281.         }
  1282.         var action=this.tree.key[this.options.modifier] ? 'copy' : 'move';
  1283.         if(this.where=='inside' && !target.isOpen()){
  1284.             target.toggle();
  1285.             if(target.$loading){
  1286.                 var self=this;
  1287.                 var onLoad=function(){
  1288.                     self.tree[action](current, target, where);
  1289.                     self.fireEvent('complete');
  1290.                     target.removeEvent('load',onLoad);
  1291.                 }
  1292.                 target.addEvent('load',onLoad);
  1293.                 return;
  1294.             }
  1295.         }
  1296.         this.tree[action](current, target, where);
  1297.         this.fireEvent('complete', [current, target, where]);
  1298.     }
  1299. });
  1300.  
  1301.  
  1302. /*
  1303. Mif.Tree.Rename
  1304. */
  1305. Mif.Tree.implement({
  1306.    
  1307.     attachRenameEvents: function(){
  1308.         this.wrapper.addEvents({
  1309.             click: function(event){
  1310.                 if($(event.target).get('tag')=='input') return;
  1311.                 this.renameComplete();
  1312.             }.bind(this),
  1313.             keydown: function(event){
  1314.                 if(event.key=='enter'){
  1315.                     this.renameComplete();
  1316.                 }
  1317.                 if(event.key=='esc'){
  1318.                     this.renameCancel();
  1319.                 }
  1320.             }.bind(this)
  1321.         });
  1322.     },
  1323.    
  1324.     disableEvents: function(){
  1325.         if(!this.eventStorage) this.eventStorage=new Element('div');
  1326.         this.eventStorage.cloneEvents(this.wrapper);
  1327.         this.wrapper.removeEvents();
  1328.     },
  1329.    
  1330.     enableEvents: function(){
  1331.         this.wrapper.removeEvents();
  1332.         this.wrapper.cloneEvents(this.eventStorage);
  1333.     },
  1334.    
  1335.     getInput: function(){
  1336.         if(!this.input){
  1337.             this.input=new Element('input').addClass('mif-tree-rename');
  1338.             this.input.addEvent('focus',function(){this.select()});
  1339.             Mif.Tree.Rename.autoExpand(this.input);
  1340.         }
  1341.         return this.input;
  1342.     },
  1343.    
  1344.     startRename: function(node){
  1345.         this.unselect();
  1346.         this.disableEvents();
  1347.         this.attachRenameEvents();
  1348.         var input=this.getInput();
  1349.         input.value=node.name;
  1350.         this.renameName=node.getDOM('name');
  1351.         this.renameNode=node;
  1352.         input.setStyle('width', this.renameName.offsetWidth+15);
  1353.         input.replaces(this.renameName);
  1354.         input.focus();
  1355.     },
  1356.    
  1357.     finishRename: function(){
  1358.         this.renameName.replaces(this.getInput());
  1359.     },
  1360.        
  1361.     renameComplete: function(){
  1362.         this.enableEvents();
  1363.         this.finishRename();
  1364.         var node=this.renameNode;
  1365.         var oldName=node.name;
  1366.         node.set({
  1367.             property:{
  1368.                 name: this.getInput().value
  1369.             }
  1370.         });
  1371.         this.fireEvent('rename', [node, node.name, oldName]);
  1372.         this.select(node);
  1373.     },
  1374.    
  1375.     renameCancel: function(){
  1376.         this.enableEvents();
  1377.         this.finishRename();
  1378.         this.select(this.renameNode);
  1379.     }
  1380.    
  1381. });
  1382.  
  1383. Mif.Tree.Node.implement({
  1384.    
  1385.     rename: function(){
  1386.         this.tree.startRename(this);
  1387.     }
  1388.    
  1389. });
  1390.  
  1391. Mif.Tree.Rename={
  1392.    
  1393.     autoExpand: function(input){
  1394.         var span=new Element('span').addClass('mif-tree-rename').setStyles({
  1395.             position: 'absolute',
  1396.             left: -2000,
  1397.             top:0,
  1398.             padding: 0
  1399.         }).injectInside(document.body);
  1400.         input.addEvent('keydown',function(event){
  1401.             (function(){
  1402.             input.setStyle('width',Math.max(20, span.set('html', input.value.replace(/\s/g,'&nbsp;')).offsetWidth+15));
  1403.             }).delay(10);
  1404.         });
  1405.     }
  1406.    
  1407. }
  1408.  
  1409.  
  1410. /*
  1411. Mif.Tree.Row
  1412. */
  1413. Mif.Tree.implement({
  1414.  
  1415.     initRows: function(){
  1416.         this.addRowWrapper();
  1417.         this.addEvent('drawRoot',function(){
  1418.             new Element('div',{'id':'mif-tree-row-'+this.root.UID, "class": 'mif-tree-row'}).injectInside(this.rowWrapper);
  1419.             new Element('div').addClass('mif-tree-row-container').injectInside(this.rowWrapper);
  1420.         }.bind(this));
  1421.         this.addEvent('drawChildren',function(node){
  1422.             Mif.Tree.Draw.rowChildren(node);
  1423.         });
  1424.         this.addEvent('toggle',function(node, state){
  1425.             node.getRowDOM('container').style.display=state ? 'block' : 'none';
  1426.         });
  1427.         this.addEvent('selectChange',function(node, state){
  1428.             node.getRowDOM('node')[(state ? 'add' : 'remove') + 'Class']('mif-tree-row-selected');
  1429.         });
  1430.         this.addEvent('hover', function(node, target, state){
  1431.             if(target!='node'||!node) return;
  1432.             var domNode=node.getRowDOM('node');
  1433.             var action=(state=='over' ? 'add' : 'remove') +'Class';
  1434.             domNode[action]('mif-tree-row-hover');
  1435.             if(node.state.selected) domNode[action]('mif-tree-row-hover-selected');
  1436.         }.bind(this));
  1437.         this.addEvent('structureChange', function(from, to, where, type){
  1438.             if(type=='copy'){
  1439.                 var dom=Mif.Tree.Draw.row(from);
  1440.                 var fromNode=dom.getFirst(), fromContainer = dom.getLast();
  1441.             }else{
  1442.                 var fromNode=from.getRowDOM('node'), fromContainer=from.getRowDOM('container');
  1443.             }
  1444.             this.injectRowDOM(fromNode, fromContainer, to, where);
  1445.         });
  1446.         this.addEvent('remove', function(node){
  1447.             node.getRowDOM('node').destroy();
  1448.         });
  1449.         this.addEvent('updateNode',function(node){
  1450.             node.getRowDOM('container').style.display=node.isOpen() ? 'block' : 'none';
  1451.         });
  1452.     },
  1453.    
  1454.     injectRowDOM: function(fromNode, fromContainer, to, where){
  1455.         var toNode=to.getRowDOM('node'), toContainer=to.getRowDOM('container');
  1456.         switch(where){
  1457.             case 'inside':
  1458.                 fromNode.injectInside(toContainer);
  1459.                 fromContainer.injectInside(toContainer);
  1460.                 break;
  1461.             case 'before':
  1462.                 fromNode.injectBefore(toNode);
  1463.                 fromContainer.injectBefore(toNode);
  1464.                 break;
  1465.             case 'after':
  1466.                 fromNode.injectAfter(toContainer);
  1467.                 fromContainer.injectAfter(fromNode);
  1468.                 break;
  1469.         }
  1470.         this.updateHover();
  1471.     },
  1472.    
  1473.     addRowWrapper: function(){
  1474.         var wrapper=this.wrapper;
  1475.         var rowWrapper=new Element('div').injectTop(this.container).addClass('mif-tree-row-wrapper');
  1476.         this.rowWrapper=rowWrapper;
  1477.         wrapper.addEvent('scroll', function(event){//sync scroll
  1478.             rowWrapper.scrollTop=wrapper.scrollTop;
  1479.         });
  1480.         if(Browser.Engine.presto){
  1481.             wrapper.addEvent('mousewheel',function(){
  1482.                 (function(){rowWrapper.scrollTop=wrapper.scrollTop;}).delay(50);
  1483.             });
  1484.         }
  1485.     }
  1486.  
  1487. });
  1488.  
  1489. Mif.Tree.Draw.rowChildren=function(node){
  1490.     if(node.tree.forest && !node.getParent()){
  1491.         var container=node.tree.rowWrapper;
  1492.     }else{
  1493.         var container=node.getRowDOM('container');
  1494.     }
  1495.     var html=[];
  1496.     var children=node.children;
  1497.     for( var i=children.length; i--; i>=0 ){
  1498.         var child=children[i];
  1499.         html.unshift('<div id="mif-tree-row-',child.UID,'" class="mif-tree-row"></div><div class="mif-tree-row-container"></div>');
  1500.     }
  1501.     container.set('html',html);
  1502. }
  1503.  
  1504. Mif.Tree.Draw.row=function(node){
  1505.     return new Element('div').set('html', '<div id="mif-tree-row-',node.UID,'" class="mif-tree-row"></div><div class="mif-tree-row-container"></div>');
  1506. }
  1507.  
  1508.  
  1509.  
  1510. Mif.Tree.Node.implement({
  1511.  
  1512.     getRowDOM: function(what){
  1513.         var node=$('mif-tree-row-'+this.UID);
  1514.         if(what=='node') return node;
  1515.         if(what=='container') return node.getNext();
  1516.     }
  1517.  
  1518. });
  1519.  
  1520.  
  1521. /*
  1522. Mif.Tree.Checkbox
  1523. */
  1524. Mif.Tree.implement({
  1525.  
  1526.     initCheckbox: function(type){
  1527.         this.checkboxType=type||'simple';
  1528.         this.dfltState.checked='unchecked';
  1529.         this.wrapper.addEvent('click',this.checkboxClick.bindWithEvent(this));
  1530.         if(this.checkboxType=='simple') return;
  1531.         this.addEvent('loadChildren', function(node){
  1532.             if(node.state.checked=='unchecked') return;
  1533.             node.recursive(function(){
  1534.                 this.state.checked='checked';
  1535.             });
  1536.         });
  1537.     },
  1538.    
  1539.     checkboxClick: function(event){
  1540.         if(this.mouse.target!='checkbox') {return;}
  1541.         this.mouse.node['switch']();
  1542.     },
  1543.    
  1544.     getChecked: function(){
  1545.         var checked=[];
  1546.         this.root.recursive(function(){
  1547.             if(this.state.checked) checked.push(checked);
  1548.         });
  1549.         return checked;
  1550.     }
  1551.  
  1552. });
  1553.  
  1554. Mif.Tree.Node.implement({
  1555.  
  1556.     'switch' : function(state){
  1557.         if(this.state.checked==state) return;
  1558.         var type=this.tree.checkboxType;
  1559.         var checked=(this.state.checked=='checked') ? 'unchecked' : 'checked';
  1560.         var setState=function(node, state){
  1561.             var oldState=node.state.checked;
  1562.             node.state.checked=state;
  1563.             if(!node.parentNode || (node.parentNode && node.parentNode.$draw)){
  1564.                 node.getDOM('checkbox').removeClass('mif-tree-node-'+oldState).addClass('mif-tree-node-'+state);
  1565.             }
  1566.         };
  1567.         if(type=='simple'){
  1568.             setState(this, checked);
  1569.             return false;
  1570.         };
  1571.         this.recursive(function(){
  1572.             setState(this, checked);
  1573.         });
  1574.         function setParentCheckbox(node){
  1575.             if(!node.parentNode || (node.tree.forest && !node.parentNode.parentNode)) return;
  1576.             var parent=node.parentNode;
  1577.             var state='';
  1578.             var children=parent.children;
  1579.             for(var i=children.length; i--; i>0){
  1580.                 var child=children[i];
  1581.                 var childState=child.state.checked;
  1582.                 if(childState=='partially'){
  1583.                     state='partially';
  1584.                     break;
  1585.                 }else if(childState=='checked'){
  1586.                     if(state=='unchecked'){
  1587.                         state='partially';
  1588.                         break;
  1589.                     }
  1590.                     state='checked';
  1591.                 }else{
  1592.                     if(state=='checked'){
  1593.                         state='partially';
  1594.                         break;
  1595.                     }else{
  1596.                         state='unchecked';
  1597.                     }
  1598.                 }
  1599.             }
  1600.             if(parent.state.checked==state){return;};
  1601.             setState(parent, state);
  1602.             setParentCheckbox(parent);
  1603.         }
  1604.         setParentCheckbox(this);
  1605.     }
  1606.  
  1607. });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement