[转帖]Mootools源码分析-44 -- Sortables

原帖地址:http://space.flash8.net/space/?uid-18713-action-viewspace-itemid-409214

原作者:我佛山人

 

代码
//拖放排序类
//
演示:http://demos.mootools.net/Sortables
var Sortables = new Class({

    
//继承自Events和Options,UI插件的特点
    Implements: [Events, Options],

    options: {
/*
        //排序事件
        onSort: $empty,
        //开始事件
        onStart: $empty,
        //完成事件
        onComplete: $empty,
*/
        snap: 
4,
        
//透明度
        opacity: 1,
        
//是否复制
        clone: false,
        
//是否使用形变动画
        revert: false,
        
//拖动句柄对象
        handle: false,
        
//约束
        constrain: false
    },

    
//构造函数
    initialize: function(lists, options)    {
        
//设置配置参数
        this.setOptions(options);
        
this.elements = [];
        
this.lists = [];
        
this.idle = true;

        
//列表项可以单个,也可以是符合
        this.addLists($$($(lists) || lists));
        
//如果不克隆副本,则不需要变形动画
        if (!this.options.clone)    this.options.revert = false;
        
//如果指定变形动画效果
        if (this.options.revert) this.effect = new Fx.Morph(null, $merge({duration: 250, link: 'cancel'}, this.options.revert));
    },

    
//附加事件
    attach: function()    {
        
this.addLists(this.lists);
        
return this;
    },

    
//移除事件
    detach: function()    {
        
this.lists = this.removeLists(this.lists);
        
return this;
    },

    
//添加列表项
    addItems: function()    {
        
//给参数降维再遍历
        Array.flatten(arguments).each(function(element)    {
            
//加到数组
            this.elements.push(element);
            
//将关联本列表项的start方法缓存
            var start = element.retrieve('sortables:start'this.start.bindWithEvent(this, element));

            
//如果指定拖动句柄,从当前列表项中获取,否则将当前列表项作为句柄
            //然后给句柄添加鼠标按下事件,触发start方法
            (this.options.handle ? element.getElement(this.options.handle) || element : element).addEvent('mousedown', start);
        }, 
this);
    
return this;
    },

    
//添加列表
    addLists: function()    {
        
//给参数降维再遍历
        Array.flatten(arguments).each(function(list)    {
            
this.lists.push(list);
            
//添加当前列表下的项
            this.addItems(list.getChildren());
        }, 
this);
        
return this;
    },

    
//移除列表项
    removeItems: function()    {
        
//用于返回的数组
        var elements = [];
        
//给参数降维再遍历
        Array.flatten(arguments).each(function(element)    {
            
//加到返回数组
            elements.push(element);
            
//从数组中删除
            this.elements.erase(element);
            
//取缓存中的事件绑定方法
            var start = element.retrieve('sortables:start');
            
//类似添加列表项时的判断,找到句柄后移除事件
            (this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start);
        }, 
this);
        
//返回移除的列表项集合
        return $$(elements);
    },

    
//移除列表
    removeLists: function()    {
        
//用于返回的数组
        var lists = [];
        
//给参数降维再遍历
        Array.flatten(arguments).each(function(list)    {
            
//加到返回数组
            lists.push(list);
            
//从数组中删除
            this.lists.erase(list);
            
//移除当前列表的所有列表项,因为removeItems方法中自动给参数降维,所以可以直接传数组
            this.removeItems(list.getChildren());
        }, 
this);
        
//返回移除的列表集合
        return $$(lists);
    },

    
//获取复制的对象
    getClone: function(event, element)    {
        
//如果指定不复制,返回新创建的div并插入到body
        if (!this.options.clone)    return new Element('div').inject(document.body);
        
//如果参数为函数,修改其调用的上下文指向并传送指定参数
        if ($type(this.options.clone) == 'function')    return this.options.clone.call(this, event, element, this.list);
        
//剩下的是指定为复制的情况
        //复制当前列表项并修改其样式
        return element.clone(true).setStyles({
                
'margin''0px',
                
'position''absolute',
                
'visibility''hidden',
                
'width': element.getStyle('width')
        }).inject(
this.list).position(element.getPosition(element.offsetParent));
    },

    
//获取可放落的对象
    getDroppables: function()    {
        
//取当前列表下的所有列表项
        var droppables = this.list.getChildren();
        
//如果没有约束,则其它列表也作为可放落对象
        if (!this.options.constrain)    droppables = this.lists.concat(droppables).erase(this.list);
        
//从可放落对象集合中排除当前对象及其克隆
        return droppables.erase(this.clone).erase(this.element);
    },

    
//插入
    insert: function(dragging, element)    {
        
//指定插入位置
        var where = 'inside';
        
if (this.lists.contains(element))    {
            
this.list = element;
            
this.drag.droppables = this.getDroppables();
        }    
else    {
            where 
= this.element.getAllPrevious().contains(element) ? 'before' : 'after';
        }
        
this.element.inject(element, where);
        
this.fireEvent('onSort', [this.element, this.clone]);
    },

    
//开始操作
    start: function(event, element)    {
        
//如果当前非闲置状态,退出
        if (!this.idle)    return;
        
//设置闲置状态标识
        this.idle = false;
        
//当前操作对象
        this.element = element;
        
//获取当前透明度
        this.opacity = element.get('opacity');
        
//获取当前操作对象所在的列表对象
        this.list = element.getParent();
        
//获取当前操作对象的克隆对象
        this.clone = this.getClone(event, element);

        
//创建拖放对象
        this.drag = new Drag.Move(this.clone, {
            snap: 
this.options.snap,
            
//拖放范围,如果指定constrain参数为true才会取当前对象所在的列表对象
            container: this.options.constrain && this.element.getParent(),
            
//获取可放落的对象
            droppables: this.getDroppables(),
            
//onSnap事件监听
            onSnap: function()    {
                
//停止事件冒泡及返回值
                event.stop();
                
//显示克隆对象
                this.clone.setStyle('visibility''visible');
                
//设置透明度
                this.element.set('opacity'this.options.opacity || 0);
                
//触发onStart事件
                this.fireEvent('onStart', [this.element, this.clone]);
            }.bind(
this),
            
//拖动进入事件
            onEnter: this.insert.bind(this),
            
//取消拖动事件
            onCancel: this.reset.bind(this),
            
//拖动完成事件
            onComplete: this.end.bind(this)
        });
        
//插入克隆对象到当前对象前面
        this.clone.inject(this.element, 'before');
        
//开始拖动
        this.drag.start(event);
    },

    
//结束拖动排序
    end: function()    {
        
//移除事件
        this.drag.detach();
        
//恢复透明度
        this.element.set('opacity'this.opacity);
        
//如果使用形变动画(主要是外形尺寸和坐标位置上的)
        if (this.effect)    {
            
//获取尺寸
            var dim = this.element.getStyles('width''height');
            
//计算位置
            var pos = this.clone.computePosition(this.element.getPosition(this.clone.offsetParent));
            
//将当前克隆对象作为形变对象
            this.effect.element = this.clone;
            
//开始动画效果
            this.effect.start({
                top: pos.top,
                left: pos.left,
                width: dim.width,
                height: dim.height,
                opacity: 
0.25
                
//动画完成后的后续操作
            }).chain(this.reset.bind(this));
        }    
else    {
            
//后续操作
            this.reset();
        }
    },

    
//重置
    reset: function()    {
        
//设置闲置状态标记
        this.idle = true;
        
//清除克隆对象
        this.clone.destroy();
        
//触发onComplete事件
        this.fireEvent('onComplete'this.element);
    },

    
//序列化
    serialize: function()    {
        
//使用Array.link处理参数
        var params = Array.link(arguments, {modifier: Function.type, index: $defined});
        
//遍历列表集
        var serial = this.lists.map(function(list)    {
            
//遍历各列表的列表项,如果参数中传有函数的引用,使用该函数处理列表项,否则默认返回id
            return list.getChildren().map(params.modifier || function(element)    {
                
//取列表项的id
                return element.get('id');
            }, 
this);
        }, 
this);

        
//参数中传送的索引值
        var index = params.index;
        
//如果列表集中只有一项,则索引值为0
        if (this.lists.length == 1) index = 0;
        
//如果索引存在并且值在合法范围内,返回指定索引值列表的序列化结果,否则返回整个列表集的序列化结果
        return $chk(index) && index >= 0 && index < this.lists.length ? serial[index] : serial;
    }
});

 

posted @ 2009-12-01 19:35  webgis松鼠  阅读(457)  评论(0编辑  收藏  举报