原帖地址: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;
}
});
// 演示: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;
}
});