基于Q.js的UI库,包括遮罩层、弹出框、下拉列表、右键菜单、颜色选择器、选项卡、进度条、数据分页、无缝滚动等。
/*
* Q.UI.Box.js (包括遮罩层、拖动、弹出框)
* https://github.com/devin87/Q.UI.js
* author:devin87@qq.com
* update:2022/04/14 11:10
*/
(function (undefined) {
"use strict";
var window = Q.G,
document = window.document,
isObject = Q.isObject,
isFunc = Q.isFunc,
isUNum = Q.isUNum,
isUInt = Q.isUInt,
def = Q.def,
fire = Q.fire,
async = Q.async,
extend = Q.extend,
makeArray = Q.makeArray,
getStyle = Q.getStyle,
setStyle = Q.setStyle,
getFirst = Q.getFirst,
getNext = Q.getNext,
//getLast = Q.getLast,
setWidth = Q.width,
setHeight = Q.height,
createEle = Q.createEle,
removeEle = Q.removeEle,
cssShow = Q.show,
cssHide = Q.hide,
isHidden = Q.isHidden,
addClass = Q.addClass,
setCssIfNot = Q.setCssIfNot,
setCenter = Q.setCenter,
setInputError = Q.setInputError,
setInputDefault = Q.setInputDefault,
clearSelection = Q.clearSelection,
query = Q.query,
factory = Q.factory,
browser_ie = Q.ie,
isIE6 = browser_ie < 7,
view = Q.view,
Listener = Q.Listener,
E = Q.event;
//------------------------- Mask -------------------------
var GLOBAL_MASK_SETTINGS = { color: "#999", opacity: 0.3 };
//构造器:遮罩层
function MaskBox(ops) {
var box = createEle("div", "x-mask");
Q.body.appendChild(box);
if (isIE6) box.style.height = view.getScrollHeight() + "px";
this.box = box;
this.set(extend(ops || {}, GLOBAL_MASK_SETTINGS));
this.count = isHidden(box) ? 0 : 1;
}
factory(MaskBox).extend({
//设置透明度
opacity: function (opacity) {
return this.set({ opacity: opacity });
},
//设置遮罩层样式 eg:{color,zIndex,width,height}
set: function (ops) {
var box = this.box;
Object.forEach(ops, function (key, value) {
if (value !== undefined) setStyle(box, key != "color" ? key : "backgroundColor", value);
});
return this;
},
//显示遮罩层
show: function () {
if (this.removed) {
Q.body.appendChild(this.box);
this.removed = false;
}
cssShow(this.box);
this.count++;
return this;
},
//隐藏遮罩层
hide: function () {
if (this.count > 0) this.count--;
if (this.count <= 0) cssHide(this.box);
return this;
},
//移除遮罩层
remove: function () {
removeEle(this.box);
this.removed = true;
this.count = 0;
return this;
}
});
var GLOBAL_MASK_BOX;
//获取通用遮罩层(默认配置)
function getMaskBox() {
if (GLOBAL_MASK_BOX) {
GLOBAL_MASK_BOX.show();
return GLOBAL_MASK_BOX;
}
GLOBAL_MASK_BOX = new MaskBox();
return GLOBAL_MASK_BOX;
}
//修改遮罩层全局配置
function maskSetup(ops, isUpdateUI) {
extend(GLOBAL_MASK_SETTINGS, ops, true);
if (isUpdateUI && GLOBAL_MASK_BOX) GLOBAL_MASK_BOX.reset(ops);
}
//------------------------- Drag -------------------------
//获取所有顶层窗口(parent)
function getTops(w) {
var parents = [], cwin = w || window;
while (cwin.top != cwin) {
var pwin = cwin.parent;
parents.push(pwin);
cwin = pwin;
}
return parents;
}
/*//获取所有内嵌框架
function _getFrames(w, ret) {
var frames = w.frames, len = frames.length;
if (len <= 0) return;
for (var i = 0; i < len; i++) {
ret.push(frames[i]);
_getFrames(frames[i], ret);
}
}
//获取所有内嵌框架
function getFrames(w) {
var frames = [];
_getFrames(w || window, frames);
return frames;
}*/
var DEF_INDEX = 1000, CURRENT_INDEX = DEF_INDEX, MASK_FOR_FRAME;
//基础拖动对象
function DragX(init) {
var self = this;
fire(init, self);
self._apis = [];
self.start();
}
factory(DragX).extend({
//开始拖动
start: function () {
var self = this,
ops = self.ops,
win = ops.scope || window,
document = win.document,
ele = ops.ele,
target = ops.target || ele,
autoIndex = ops.autoIndex !== false,
autoMask = !!ops.autoMask,
autoCss = ops.autoCss !== false,
autoCursor = ops.autoCursor !== false,
zIndex = ele.nodeType == 1 ? +getStyle(ele, "z-index") : 0,
doDown = self.doDown,
doMove = self.doMove,
doUp = self.doUp,
onCheck = ops.onCheck,
onDown = ops.onDown,
onMove = ops.onMove,
onUp = ops.onUp,
topWins = getTops(win),
//frameWins,
hasCapture = !!ele.setCapture,
hasListen = !hasCapture && topWins.length > 0; //是否需要监听其它窗口
if (zIndex >= CURRENT_INDEX) CURRENT_INDEX = zIndex + 1;
//初始化元素状态
if (autoCss) setCssIfNot(ele, "position", "absolute");
if (autoCss && autoCursor) setCssIfNot(target, "cursor", "move");
//设置元素居中
if (ops.center) {
setCenter(ele);
self._api_resize = E.add(win, "resize", function () {
setCenter(ele);
});
}
//鼠标按下事件
var mousedown = function (e) {
if (isFunc(onCheck) && onCheck.call(self, e) === false) return;
if (autoIndex) {
var _zIndex = +getStyle(ele, "z-index") || 0;
if (_zIndex < CURRENT_INDEX) {
CURRENT_INDEX++;
ele.style.zIndex = CURRENT_INDEX;
}
}
self._unbind();
if (hasCapture) {
ele.setCapture();
self._bind(ele, "losecapture", mouseup);
} else {
self._bind(win, "blur", mouseup);
}
self._bind(document, {
"mousemove": mousemove,
"mouseup": mouseup
});
if (hasListen) self._bind(topWins, "mouseup", mouseup);
//frameWins = getFrames(win);
//var hasMask = autoMask && !hasCapture && frameWins.length > 0;
//创建一个遮罩层,在遮罩层上拖动
//1. 避免某些浏览器在经过iframe时鼠标事件响应不正常
//2. 避免拖动过程产生文本选区
if (autoMask) {
if (!MASK_FOR_FRAME) MASK_FOR_FRAME = new MaskBox({ color: null, opacity: null, zIndex: 999999 });
if (isIE6) MASK_FOR_FRAME.set({ height: view.getScrollHeight() });
}
//清除文本选区
async(clearSelection, 20);
fire(doDown, self, e);
fire(onDown, self, e);
};
//鼠标移动事件
var mousemove = function (e) {
if (self._pause) return;
fire(doMove, self, e);
fire(onMove, self, e);
};
//鼠标释放事件
var mouseup = function (e) {
self._unbind();
if (hasCapture) ele.releaseCapture();
if (autoMask && MASK_FOR_FRAME) {
MASK_FOR_FRAME.remove();
MASK_FOR_FRAME = null;
}
clearSelection();
fire(doUp, self, e);
fire(onUp, self, e);
};
self._up = mouseup;
self._api = E.add(target, "mousedown", mousedown);
return self;
},
//绑定事件
_bind: function () {
this._apis.push(E.add.apply(E, arguments));
},
//清理事件绑定
_unbind: function () {
var apis = this._apis;
if (apis.length > 0) {
apis.forEach(function (api) { api.off(); });
this._apis = [];
}
},
//暂停拖动
pause: function (flag) {
var self = this;
self._pause = flag;
self._up && self._up();
return self;
},
//停止拖动
stop: function () {
var self = this,
api = self._api;
self._unbind();
if (api) {
api.off();
self._api = null;
}
return self;
},
destroy: function () {
var self = this,
api_resize = self._api_resize;
self.stop();
if (api_resize) {
api_resize.off();
self._api_resize = null;
}
return self;
}
});
var ELE_DRAG_SHADOW;
//设置拖动
function setDrag(ele, ops) {
return new DragX(function () {
if (ele.nodeType != 1) {
ops = ele;
ele = ops.ele;
} else {
ops = ops || {};
ops.ele = ele;
}
var base = this,
range = ops.range || { x: 0, y: 0 },
hasShadow = !!ops.shadow,
w = ele.offsetWidth,
h = ele.offsetHeight,
target = ele,
startLeft, startTop,
startX, startY, movedX, movedY, _isX, _isY;
if (hasShadow) {
if (!ELE_DRAG_SHADOW) {
ELE_DRAG_SHADOW = createEle("div", "x-drag-shadow");
Q.body.appendChild(ELE_DRAG_SHADOW);
}
target = ELE_DRAG_SHADOW;
}
//实现ops接口
base.ops = ops;
//实现doDown接口
base.doDown = function (e) {
startX = e.clientX;
startY = e.clientY;
var offset = hasShadow ? $(ele).offset() : $(ele).position();
startLeft = offset.left;
startTop = offset.top;
if (hasShadow) {
Object.forEach({ left: startLeft, top: startTop, width: w, height: h }, function (key, value) {
target.style[key] = value + "px";
});
//cssShow(target);
}
};
//实现doMove接口
base.doMove = function (e) {
cssShow(target);
if (_isX) {
movedX = e.clientX - startX;
var x = startLeft + movedX;
if (range) {
if (x < range.x) x = range.x;
else if (range.w && x + w > range.x + range.w) x = range.x + range.w - w;
}
target.style.left = x + "px";
}
if (_isY) {
movedY = e.clientY - startY;
var y = startTop + movedY;
if (range) {
if (y < range.y) y = range.y;
else if (range.h && y + h > range.y + range.h) y = range.y + range.h - h;
}
target.style.top = y + "px";
}
};
if (hasShadow) {
base.doUp = function () {
cssHide(target);
ele.style.left = (ele.offsetLeft + movedX) + "px";
ele.style.top = (ele.offsetTop + movedY) + "px";
};
}
//设置拖动方向
base.setLock = function (isX, isY) {
_isX = isX;
_isY = isY;
return base.pause(!isX && !isY);
};
//设置拖动范围
base.setRange = function (x, y, w, h) {
range = isObject(x) ? x : { x: x, y: y, w: w, h: h };
return base;
};
base.setLock(ops.isX !== false, ops.isY !== false);
});
}
$.fn.extend({
drag: function (ops) {
return this.each(function (i, el) {
setDrag(el, ops);
});
}
});
//------------------------- Box -------------------------
//Box全局事件处理
var listener_box = new Listener(["init", "show", "hide", "remove"]);
//接口,构造器:Box对象
function Box(init) {
this._es = [];
fire(init, this);
listener_box.trigger("init", [this]);
}
factory(Box).extend({
//在弹出框内查找对象
find: function (pattern, context) {
return typeof pattern == "string" ? query(pattern, context || this.box) : makeArray(pattern);
},
//在弹出框内查找对象
$: function (pattern, context) {
return $(this.find(pattern, context));
},
//获取弹出框内查找到的第一个对象
get: function (pattern, context) {
return this.find(pattern, context)[0];
},
//触发回调函数
fire: function () {
fire(this.callback, this, this.data);
return this;
},
//获取事件回调函数
getEventCallback: function (fn, data) {
var self = this;
if (fn == "hide") return function () { self.data = data; self.hide(); };
if (fn == "remove") return function () { self.data = data; self.remove(); };
return fn;
},
//绑定事件,同 Event.add,不过会缓存事件句柄,用于统一销毁
bind: function (selector, types, fn, data) {
var self = this;
self._es.push(E.add(self.find(selector), types, self.getEventCallback(fn, data)));
return self;
},
//事件代理,将事件代理到box上执行
on: function (types, selector, fn, data) {
var self = this;
self._es.push(E.add(self.box, types, selector, self.getEventCallback(fn, data)));
return self;
},
//显示
show: function () {
var self = this;
if (self.onShow) self.onShow();
cssShow(self.box);
if (self.mbox) self.mbox.show();
listener_box.trigger("show", [self]);
return self;
},
//隐藏
hide: function () {
var self = this;
cssHide(self.box);
if (self.mbox) self.mbox.hide();
if (self.onHide) self.onHide();
listener_box.trigger("hide", [self]);
return self.fire();
},
//自动切换显示或隐藏
toggle: function () {
return isHidden(this.box) ? this.show() : this.hide();
},
//移除
remove: function () {
var self = this;
if (!self.box) return;
removeEle(self.box);
//遮罩层
if (self.mbox) self.mbox.hide();
//拖动框
if (self.dr) self.dr.destroy();
//解除绑定的事件
self._es.forEach(function (api) {
api.off();
});
self.box = self.mbox = self.dr = null;
if (self.onRemove) self.onRemove();
listener_box.trigger("remove", [self]);
return self.fire();
}
});
Box.alias({
"$": "query",
"remove": "destroy"
});
//添加全局事件 type: init、show、hide、remove
Box.on = function (type, fn) {
listener_box.add(type, fn);
return Box;
};
//弹出层语言
var LANG_BOX = {
titleBox: "弹出框",
titleAlert: "提示信息",
titleConfirm: "确认信息",
titlePrompt: "输入信息",
titleClose: "点击关闭",
titleLoading: "加载数据",
buttonSubmit: "确定",
buttonCancel: "取消",
textLoading: "正在加载数据,请稍后…"
};
//配置弹出层
function setBoxLang(langs) {
extend(LANG_BOX, langs, true);
}
//构造器:弹出层
function WinBox(ops) {
ops = ops || {};
return new Box(function () {
var self = this,
width = ops.width,
height = ops.height,
maxHeight = def(ops.maxHeight, view.getHeight() - 60),
isDrag = ops.drag !== false,
isCenter = isDrag && ops.center !== false,
className = ops.className;
self.ops = ops;
self.callback = ops.callback;
var html =
'<div>' +
'<h2>' + (ops.title || LANG_BOX.titleBox) + '</h2>' +
'<a title="' + LANG_BOX.titleClose + '">X</a>' +
'</div>' +
'<div>' +
'<div>' +
(ops.html || '') +
'</div>' +
'</div>';
//解决ie6中 div 无法遮盖 select 的问题
if (isIE6) {
html += '<iframe style="position: absolute;top:0;left:0;z-index:-1;" scrolling="no" frameborder="0"></iframe>';
}
var box = createEle("div", "x-box" + (className ? " " + className : ""), html);
Q.body.appendChild(box);
self.box = box;
var zIndex = ops.zIndex || 0;
if (CURRENT_INDEX > DEF_INDEX) zIndex = Math.max(zIndex, CURRENT_INDEX);
if (zIndex) box.style.zIndex = zIndex;
var boxHead = getFirst(box),
boxMain = getNext(boxHead);
//设置标题
self.setTitle = function (title) {
$(".x-title", boxHead).html(title);
return self;
};
//设置宽度
self.setWidth = function (width) {
ops.width = width;
setWidth(box, width);
fire(ops.resize, self);
return self;
};
//设置高度
self.setHeight = function (height) {
ops.height = height;
setHeight(boxMain, height - boxHead.offsetHeight - 20);
return self;
};
//设置最大高度,超出后出现滚动条
self.setMaxHeight = function (maxHeight) {
ops.maxHeight = maxHeight;
var height = ops.height;
if (isUNum(height) && height > maxHeight) height = maxHeight;
if (box.scrollHeight > maxHeight) {
height = maxHeight;
addClass(box, "x-box-auto");
}
if (isUNum(height)) self.setHeight(height);
if (isCenter) setCenter(box);
fire(ops.resize, self);
return self;
};
//自动调整高度以适应maxHeight
self.autoHeight = function () {
var maxHeight = ops.maxHeight;
if (!maxHeight) return self;
var height_head = self.get(".x-head").offsetHeight,
height_main = maxHeight - height_head - 20,
max_height_main = maxHeight - height_head;
boxMain.style.height = boxMain.scrollHeight > max_height_main ? height_main + "px" : "auto";
if (isCenter) setCenter(box);
return self;
};
//兼容性考虑,width最好指定固定值
if (isUNum(width)) setWidth(box, width);
if (boxHead.offsetWidth < 10) setWidth(boxHead, box.offsetWidth);
//高度设置
if (maxHeight) self.setMaxHeight(maxHeight);
else if (isUNum(height)) self.setHeight(height);
//遮罩层
if (ops.mask !== false) self.mbox = ops.mask == "new" ? new MaskBox() : getMaskBox();
var action_close = ops.close || "hide",
callback_close = self.getEventCallback(action_close);
//点击关闭事件
self.bind(".x-close", "click", action_close);
//按ESC关闭事件
if (ops.esc !== false) {
self.bind(document, "keyup", function (e) {
if (e.keyCode == 27) callback_close();
});
}
//指定时间后自动关闭弹出框
var time = ops.time;
if (isUInt(time)) async(callback_close, time);
fire(ops.init, self, box, ops);
//拖动
if (isDrag) {
self.dr = setDrag(box, {
target: boxHead,
center: isCenter,
shadow: ops.shadow,
autoMask: true,
//1.由于拖动会创建一个遮罩层,点击关闭时不会触发 .x-close 的click事件,此处检查点击元素,只有非 .x-close 元素才会执行拖动操作
//2.亦可将 .x-close 的click事件替换为mousedown事件以优先执行,可不必传入onCheck
onCheck: function (e) {
var target = e.target;
return target && target.className != "x-close";
}
});
}
$(".x-ie-fix", box).width(box.offsetWidth - 2).height(box.offsetHeight - 2);
});
}
//创建对话框
function createDialogBox(ops) {
if (!ops.width) ops.width = 320;
var width = ops.width;
if (ops.icon || ops.iconHtml) {
var html =
'<div class="fl x-ico">' +
(ops.iconHtml || '<img alt="" src="' + ops.icon + '"/>') +
'</div>' +
'<div class="fl x-dialog"' + (isUNum(width) ? ' style="width:' + (width - 60) + 'px;"' : '') + '>' + ops.html + '</div>' +
'<div></div>';
ops.html = html;
var oldInit = ops.init;
ops.resize = function () {
var self = this,
contentWidth = self.get(".x-view").offsetWidth - self.get(".x-ico").offsetWidth;
$(".x-dialog", self.box).width(contentWidth);
};
ops.init = function (box, ops) {
var self = this;
ops.resize.call(self);
fire(oldInit, self, box, ops);
};
} else {
ops.html = '<div>' + ops.html + '</div>';
}
if (ops.bottom) ops.html += ops.bottom;
if (!ops.close) ops.close = "remove";
return WinBox(ops);
}
//获取底部模板代码
function get_bottom_html(mode, style) {
var buttonStyle = 'inline-block w-button w-' + (style || "dark"),
html =
'<div>' +
'<div class="' + buttonStyle + ' x-submit">' + LANG_BOX.buttonSubmit + '</div>' +
(mode == 2 ? '<div class="' + buttonStyle + ' x-cancel">' + LANG_BOX.buttonCancel + '</div>' : '') +
'</div>';
return html;
}
//获取弹出框配置对象
function get_dialog_ops(title, msg, fn, ops) {
if (typeof fn === "object") {
ops = fn;
fn = ops;
}
ops = extend({}, ops);
if (isFunc(fn)) ops.callback = fn;
if (!ops.title) ops.title = title;
ops.html = msg;
return ops;
}
var dialog = {
//创建自定义弹出框
createDialogBox: createDialogBox,
//提示框
alert: function (msg, fn, ops) {
ops = get_dialog_ops(LANG_BOX.titleAlert, msg, fn, ops);
//ops.icon = 'images/Q/alert.gif';
ops.iconHtml = '<div class="ico x-alert"></div>';
return createDialogBox(ops);
},
//确认框
confirm: function (msg, fn, ops) {
ops = get_dialog_ops(LANG_BOX.titleConfirm, msg, fn, ops);
if (!ops.bottom) ops.bottom = get_bottom_html(2);
ops.mask = ops.mask !== false;
var box = createDialogBox(ops);
return box.bind(".x-submit", "click", "remove", true).bind(".x-cancel", "click", "remove", false);
},
prompt: function (msg, fn, ops) {
ops = get_dialog_ops(LANG_BOX.titlePrompt, undefined, fn, ops);
fn = ops.callback;
ops.callback = undefined;
var html =
'<div>' + msg + '</div>' +
'<div><input type="' + (ops.pwd ? 'password' : 'text') + '" /></div>';
ops.html = html;
if (!ops.bottom) ops.bottom = get_bottom_html(2);
var box = createDialogBox(ops),
input = box.get(".x-input>input");
input.focus();
input.value = def(ops.value, "");
var submit = function () {
var v = fire(fn, input, input.value);
if (v !== false) box.remove();
else setInputError(input);
};
//输入框快捷提交
box.bind(input, "keyup", function (e) {
if (e.keyCode == 13) submit();
else setInputDefault(this);
});
//确定与取消
return box.bind(".x-submit", "click", submit).bind(".x-cancel", "click", "remove");
},
bottom: get_bottom_html,
//显示加载框
showLoading: function (ops) {
ops = extend({}, ops);
if (!ops.title) ops.title = LANG_BOX.titleLoading;
if (!ops.html) ops.html = LANG_BOX.textLoading;
//ops.icon = 'images/Q/loading.gif';
ops.iconHtml = '<div class="ico x-loading"></div>';
return createDialogBox(ops);
}
};
extend(Q, dialog);
//------------------------- export -------------------------
extend(Q, {
getMaskBox: getMaskBox,
maskSetup: maskSetup,
setDrag: setDrag,
setBoxLang: setBoxLang,
MaskBox: MaskBox,
DragX: DragX,
Box: Box,
WinBox: WinBox
});
})();
/*
* Q.UI.ContextMenu.js 多级上下文菜单(右键菜单)
* https://github.com/devin87/Q.UI.js
* author:devin87@qq.com
* update:2015/11/26 17:19
*/
(function (undefined) {
"use strict";
var document = window.document,
//isObject = Q.isObject,
fire = Q.fire,
extend = Q.extend,
makeArray = Q.makeArray,
getStyle = Q.getStyle,
getOffset = Q.offset,
hasClass = Q.hasClass,
addClass = Q.addClass,
removeClass = Q.removeClass,
createEle = Q.createEle,
factory = Q.factory,
E = Q.event;
var POS_VALUE_HIDDEN = -10000;
//设置元素位置
function set_pos(el, x, y) {
if (x != undefined) el.style.left = x + "px";
if (y != undefined) el.style.top = y + "px";
}
//---------------------- 上下文菜单 ----------------------
function ContextMenu(data, ops) {
this.set(ops).init(data);
}
factory(ContextMenu).extend({
//初始化菜单
init: function (data) {
var self = this;
self.draw(data);
if (self.autoHide !== false) {
self._e0 = E.add(document, "click", function () {
self.hide();
});
}
return self;
},
//菜单设置 {x,y,rangeX,rangeY}
set: function (ops) {
extend(this, ops, true);
return this;
},
//生成所有菜单
draw: function (data) {
var self = this;
//数据重置
self._menus = [];
self._items = [];
self._map_menu = {};
self._map_item = {};
self.i = self.j = 0;
self._active = undefined;
//生成菜单
var tmp = self._tmp = [],
args;
self.drawMenu(data);
while ((args = tmp.shift())) {
self.drawMenu.apply(self, args);
}
self._tmp = null;
self.i = self.j = 0;
//主菜单
var box = self._getMenu(0).node;
self.box = box;
//------------ 菜单事件 ------------
var list_box = [];
self._menus.forEach(function (m) {
if (m && !m.linked) list_box.push(m.node);
});
//移除之前绑定的事件
if (self._e1) self._e1.off();
self._e1 = E.add(list_box, {
click: function (e) {
var el = this,
j = el._j;
if (hasClass(el, "x-disabled") || (self._getSubMenu(j) && !self.isFireAll)) return false;
var item = self._getItem(j);
fire(item.data.click, el, e, item);
fire(self.onclick, el, e, item);
self.hide();
},
mouseenter: function (e) {
var el = this,
i = el._i,
j = el._j;
self._hideSub(i, j);
addClass(el, "x-on");
var item = self._getItem(j);
fire(item.data.mouseover, el, e, item);
fire(self.onmouseover, el, e, item);
if (hasClass(el, "x-disabled")) return;
self._showSub(i, j, el);
},
mouseleave: function (e) {
var el = this,
i = el._i,
j = el._j,
sub_menu = self._getSubMenu(j);
if (hasClass(el, "x-disabled") || !sub_menu) removeClass(el, "x-on");
else if (i == 0) self._active = j;
var item = self._getItem(j);
fire(item.data.mouseout, el, e, item);
fire(self.onmouseout, el, e, item);
}
}, ".x-item");
if (self._e2) self._e2.off();
self._e2 = E.add(list_box, "mousedown", E.stop);
return self.hide();
},
//生成菜单
drawMenu: function (data, pid) {
var self = this,
list_menu = self._menus,
list_item = self._items,
map_menu = self._map_menu,
map_item = self._map_item,
box = data.box,
is_linked_box = !!box,
menu_index = self.i++;
if (!box) {
var className = data.className,
box = createEle("div", "x-panel" + (className ? " " + className : ""));
box.style.width = (data.width || 120) + "px";
}
list_menu[menu_index] = { node: box, linked: is_linked_box, j: pid, data: data };
if (pid) map_menu[pid] = menu_index;
//链接菜单
if (is_linked_box) return self;
var global_submenu = self.subMenu !== false
//循环生成菜单项
for (var i = 0, data_items = data.items; i < data_items.length; i++) {
var m = data_items[i];
if (!m) continue;
var item = document.createElement("div"),
item_index = self.j++;
item._i = menu_index;
item._j = item_index;
list_item[item_index] = { node: item, i: menu_index, j: item_index, data: m };
if (m.id != undefined) map_item[m.id] = item_index;
if (m.split) {
item.className = "x-split";
} else {
item.className = "x-item" + (m.disabled ? " x-disabled" : "");
item.x = m.x != undefined ? m.x : ""; //自定义值
var group = m.group,
has_submenu = global_submenu && group && (group.box || (group.items && group.items.length > 0)),
ico = m.ico;
var html = m.html ||
'<div>' +
(ico ? /^<.+>$/.test(ico) ? ico : '<img alt="" src="' + ico + '">' : '') +
'</div>' +
'<div' + (m.title ? ' title="' + m.title + '"' : '') + '>' +
m.text +
'</div>' +
(has_submenu ? '<div></div>' : '');
item.innerHTML = html;
if (has_submenu) self._tmp.push([group, item_index]);
}
box.appendChild(item);
}
Q.body.appendChild(box);
return self;
},
//根据菜单索引获取菜单
_getMenu: function (i) {
return this._menus[i];
},
//根据菜单项索引获取子菜单
_getSubMenu: function (j) {
var i = this._map_menu[j];
return i != undefined ? this._getMenu(i) : undefined;
},
//根据菜单项索引获取菜单项
_getItem: function (j) {
return this._items[j];
},
//根据指定的id获取菜单项
getItem: function (id) {
var j = this._map_item[id];
return j != undefined ? this._items[j] : undefined;
},
//更改菜单项的值
setItemText: function (id, text) {
var item = this.getItem(id);
if (!item) return;
item.data.text = text;
var node_text = item.node.childNodes[1];
if (node_text) node_text.innerHTML = text;
},
//处理菜单项
processItems: function (ids, fn) {
var self = this;
makeArray(ids).forEach(function (id) {
var item = self.getItem(id);
if (item) fn(item.node, item);
});
return self;
},
//处理菜单项(className)
_processItems: function (ids, fn, className) {
return this.processItems(ids, function (node) {
fn(node, className);
});
},
//启用菜单项
enableItems: function (ids) {
return this._processItems(ids, removeClass, "x-disabled");
},
//禁用菜单项
disableItems: function (ids) {
return this._processItems(ids, addClass, "x-disabled");
},
//显示菜单项
showItems: function (ids) {
return this._processItems(ids, removeClass, "hide");
},
//隐藏菜单项
hideItems: function (ids) {
return this._processItems(ids, addClass, "hide");
},
//元素智能定位
_setPos: function (menu, x, y, pbox) {
var self = this,
rangeX = self.rangeX,
rangeY = self.rangeY,
box = menu.node,
width = box.offsetWidth,
height = box.offsetHeight,
data = menu.data || {},
maxHeight = data.maxHeight;
//如果菜单高度超出最大高度,则启用垂直滚动条
if (maxHeight) {
var realHeight = Math.max(height, box.scrollHeight),
hasScrollbar = realHeight > maxHeight;
box.style.height = hasScrollbar ? maxHeight + "px" : "auto";
//是否固定宽度,默认菜单宽度会增加17px
if (!self.fixedWidth) box.style.width = (data.width + (hasScrollbar ? 17 : 0)) + "px";
height = box.offsetHeight;
}
if (x == undefined) x = self.x || 0;
if (y == undefined) y = self.y || 0;
if (rangeX && x + width > rangeX) {
x = rangeX - width;
if (pbox) x = x - pbox.offsetWidth + 3;
}
if (rangeY && y + height > rangeY) y = rangeY - height - 1;
if (x < 0) x = 0;
if (y < 0) y = 0;
set_pos(box, x, y);
self.x = x;
self.y = y;
return self;
},
//显示子菜单(如果有)
_showSub: function (i, j, el) {
var self = this,
menu = self._getMenu(i),
sub_menu = self._getSubMenu(j);
if (sub_menu) {
var node = menu.node,
sub_node = sub_menu.node,
offset = getOffset(el),
zIndex = +getStyle(node, "zIndex"),
zIndex_sub = +getStyle(sub_node, "zIndex");
if (zIndex_sub <= zIndex) sub_node.style.zIndex = zIndex + 1;
self._setPos(sub_menu, offset.left + el.offsetWidth - 2, offset.top, node);
}
self.i = i;
self.j = j;
return self;
},
//取消高亮的项
_inactive: function (j) {
var item = this._getItem(j);
if (item) removeClass(item.node, "x-on");
return this;
},
//隐藏子菜单
_hideSub: function (i, j) {
var self = this;
if (i <= self.i && j != self.j) {
var list_menu = self._menus, k;
//隐藏子一级菜单(当前菜单以后的菜单)
for (k = list_menu.length - 1; k > i; k--) {
self._hide(list_menu[k]);
}
//移除子一级菜单高亮的项
if (i < self.i) {
for (k = self.i; k > i; k--) {
var p_menu = self._getMenu(k);
self._inactive(p_menu.j);
}
}
}
//移除同一菜单里上次高亮的项(有子菜单的项)
if (i == self.i && j != self.j && self._getSubMenu(self.j)) self._inactive(self.j);
return self;
},
//隐藏指定菜单
_hide: function (menu) {
if (menu) {
var node = menu.node;
if (node.style.left != POS_VALUE_HIDDEN) set_pos(node, POS_VALUE_HIDDEN, POS_VALUE_HIDDEN);
}
return this;
},
//显示
show: function (x, y) {
return this._setPos(this._menus[0], x, y);
},
//隐藏
hide: function () {
var self = this;
self._menus.forEach(function (m) {
self._hide(m);
});
if (self._active != undefined) {
self._inactive(self._active);
self._active = undefined;
}
return self;
},
//菜单是否为隐藏状态
isHidden: function () {
var self = this,
box = self.box,
x = parseFloat(box.style.left),
y = parseFloat(box.style.top);
return x <= -box.offsetWidth || y <= -box.offsetHeight;
},
//自动切换显示或隐藏
toggle: function (x, y) {
return this.isHidden() ? this.show(x, y) : this.hide();
},
//注销菜单
destroy: function () {
var self = this;
if (self._e0) self._e0.off();
if (self._e1) self._e1.off();
self._menus.forEach(function (menu) {
menu.node.innerHTML = '';
$(menu.node).remove();
});
Object.forEach(self, function (prop) {
self[prop] = null;
});
return self;
}
});
//------------------------- export -------------------------
Q.ContextMenu = ContextMenu;
})();
/*
* Q.UI.DropdownList.js 下拉列表
* https://github.com/devin87/Q.UI.js
* author:devin87@qq.com
* update:2018/11/28 19:10
*/
(function (undefined) {
"use strict";
var document = window.document,
def = Q.def,
fire = Q.fire,
isObject = Q.isObject,
getFirst = Q.getFirst,
getLast = Q.getLast,
getWidth = Q.width,
setWidth = getWidth,
hasClass = Q.hasClass,
addClass = Q.addClass,
removeClass = Q.removeClass,
createEle = Q.createEle,
factory = Q.factory,
ie = Q.ie,
E = Q.event;
//---------------------- 下拉列表 ----------------------
function DropdownList(ops) {
ops = ops || {};
var self = this;
self.box = ops.box;
self.items = ops.items || [];
self.multiple = ops.multiple;
self.canInput = ops.canInput;
self.textProp = ops.textProp || "text";
//默认值或索引
self.value = ops.value;
self.index = ops.index || 0;
self.ops = ops;
}
factory(DropdownList).extend({
init: function () {
var self = this,
ops = self.ops,
box = self.box,
isDropdownList = !self.multiple,
canInput = self.canInput,
placeholder = ops.placeholder;
var html =
(isDropdownList ?
'<div>' +
(canInput ?
'<input type="text"' + (placeholder ? ' placeholder="' + placeholder + '"' : '') + ' />' :
'<div></div>') +
'<div>' +
'<div class="arrow arrow-down"></div>' +
'</div>' +
'</div>' : '') +
'<div class="x-panel x-sel-list' + (isDropdownList ? '' : ' x-sel-multiple') + '"></div>';
addClass(box, "x-sel");
box.innerHTML = html;
var elList = getLast(box),
elTag, elText, elArrow;
setWidth(elList, ops.width || box.offsetWidth - 2);
if (isDropdownList) {
elTag = getFirst(box);
elText = getFirst(elTag);
elArrow = getLast(elTag);
self.elText = elText;
self.elArrow = elArrow;
}
self.elList = elList;
//------------- Event --------------
//item 相关事件
var listener_item;
//下拉列表
if (isDropdownList) {
if (ops.autoHide !== false) {
E.add(document, "mousedown", function () {
self.hide();
});
E.add(box, "mousedown", function (e) {
E.stop(e, false, true);
});
}
E.add(canInput ? elArrow : elTag, "mousedown", function (e) {
self.toggle();
return false;
});
listener_item = {
//mousedown: E.stop,
mouseup: function (e) {
var index = this.x,
item = self.items[index];
fire(self.onclick, self, item, index);
if (item.disabled) return;
self.hide();
if (index != self.index) self.select(index);
},
mouseenter: function () {
var index = this.x,
item = self.items[index];
if (!item.disabled) self.active(index);
}
};
} else {
self.selectedItems = [];
self.seletedMap = {};
//多选框事件
listener_item = {
mousedown: function (e) {
var el = this;
if (hasClass(el, "x-disabled")) return;
var index = this.x,
//items = self.items,
firstItem = self.selectedItems[0],
shiftKey = e.shiftKey,
ctrlKey = e.ctrlKey;
if (shiftKey || !ctrlKey) self.clearSelect();
if (shiftKey) {
var start = firstItem ? firstItem.index : index,
end = index;
if (start > end) {
end = start;
start = index;
}
for (var i = start; i <= end; i++) {
self.active(i);
}
} else {
if (ctrlKey && self.seletedMap[index]) self.inactive(index);
else self.select(index);
}
}
};
if (ie < 10) listener_item.selectstart = E.stop;
}
E.add(elList, listener_item, ".x-item");
self.draw();
fire(self.oninit, self);
return self;
},
//绘制下拉视图
draw: function () {
var self = this,
ops = self.ops,
items = self.items,
elList = self.elList,
hasTitle = ops.hasTitle,
maxHeight = ops.maxHeight,
map = {};
elList.innerHTML = "";
items.forEach(function (m, i) {
m.index = i;
map[m.value] = m;
var text = m.text || '',
node = createEle("div", "x-item" + (m.group ? " x-sel-group" : m.disabled ? " x-disabled" : ""), text);
if (hasTitle) node.title = m.title || text.toText();
node.x = i;
m.node = node;
elList.appendChild(node);
});
self.map = map;
if (maxHeight) {
var realHeight = elList.offsetHeight;
if (realHeight > maxHeight) elList.style.height = maxHeight + "px";
}
fire(self.ondraw, self);
var value = self.value,
index = self.index;
if (value) {
var item = self.find(value);
if (item) index = item.index;
}
self.select(index);
return self.multiple ? self : self.hide();
},
//添加下拉项,类似option
add: function (text, value, title) {
this.items.push(isObject(text) ? text : { text: text, value: value, title: title });
return this;
},
//查找指定值的项 eg:{text,value,index}
find: function (value) {
return this.map[value];
},
//清除选中项
clearSelect: function () {
var self = this;
var inactive = function (item) {
if (item.node) removeClass(item.node, "selected");
};
//多选框
if (self.multiple) {
self.selectedItems.forEach(inactive);
self.selectedItems = [];
self.seletedMap = {};
} else {
inactive({ node: self._el });
}
return self;
},
//设置项高亮
active: function (index) {
var self = this,
item = self.items[index];
if (!item) return self;
var node = item.node,
flag;
//多选框
if (self.multiple) {
if (!item.disabled && !self.seletedMap[index]) {
self.selectedItems.push(item);
self.seletedMap[index] = true;
flag = true;
}
} else {
self.clearSelect();
flag = true;
self._el = node;
}
if (flag) addClass(node, "selected");
return this;
},
//取消项高亮
inactive: function (index) {
var self = this,
item = self.items[index];
if (self.multiple) {
self.selectedItems = self.selectedItems.filter(function (item) {
return item.index != index;
});
self.seletedMap[index] = undefined;
}
removeClass(item.node, "selected");
return self;
},
//选中指定索引的项
//isNotChange:是否不触发change事件
select: function (index, isNotChange) {
var self = this,
items = self.items,
item = items[index];
if (!item) return self;
self.text = def(item.text, "");
self.value = def(item.value, "");
if (self.elText) self.elText[self.canInput ? "value" : "innerHTML"] = self[self.textProp];
//多选框
self.active(index);
if (!isNotChange && index != self.index) fire(self.onchange, self, item, index);
self.index = index;
return self;
},
//显示下拉列表
show: function () {
this.elList.style.display = "";
return this.select(this.index);
},
//隐藏下拉列表
hide: function () {
this.elList.style.display = "none";
return this;
},
//自动切换显示或隐藏下拉列表
toggle: function () {
return this.elList.style.display == "none" ? this.show() : this.hide();
},
//获取当前项文本,当canInput为true时可调用此方法获取输入文本
getText: function () {
return this.canInput ? this.elText.value : this.text;
}
});
//------------------------- export -------------------------
Q.DropdownList = DropdownList;
})();
/*
* Q.UI.DataPager.js 数据分页
* https://github.com/devin87/Q.UI.js
* author:devin87@qq.com
* update:2018/12/18 10:48
*/
(function (undefined) {
"use strict";
var extend = Q.extend,
factory = Q.factory;
var LANG = {
prevPage: "<上一页",
nextPage: "下一页>",
pageSize: "每页{0}条",
totalCount: "共{0}条数据"
};
//配置语言
function setLang(langs) {
extend(LANG, langs, true);
}
//---------------------- 数据分页 ----------------------
//生成分页页码数据 eg:create_pager_bar_data(9,28,5) => [1,0,4,5,6,7,8,0,28] 0表示省略号,其它表示页码
//size:要显示的页码数量(包括省略号),不小于7的奇数
//totalPage:总页数
//page:当前页索引
function create_pager_bar_data(size, totalPage, page) {
var tmp = [1];
//只有一页的情况
if (totalPage == 1) return tmp;
//if (size < 7) size = 7;
//else if (size % 2 == 0) size++;
var first = 2, last;
//总页数小于或等于页码数量
if (totalPage <= size) {
last = totalPage;
} else {
var sideCount = (size - 5) / 2;
//左边无省略号 eg: [1,2,3,4,5,0,20]
if (page <= sideCount + 3) {
last = size - 2;
} else if (page > totalPage - sideCount - 3) {
//右边无省略号 eg: [1,0,16,17,18,19,20]
first = totalPage - size + 3; //=last-(size-4)
last = totalPage - 1;
} else {
//左右均有省略号 eg: [1,0,8,9,10,0,20]
first = page - ~~sideCount;
last = first + size - 5;
}
}
if (first > 2) tmp.push(0);
for (var i = first; i <= last; i++) tmp.push(i);
if (totalPage > size) {
if (last < totalPage - 1) tmp.push(0);
tmp.push(totalPage);
}
return tmp;
}
//构造器:分页对象
function DataPager(ops) {
var self = this;
self.ops = ops;
self.size = ops.size || 9;
self.cache = {};
if (ops.href == undefined) {
var boxNav = ops.boxNav;
if (boxNav) {
$(boxNav).on("click", "li", function () {
self.go($(this).attr("x"));
if (self.onclick) self.onclick.call(self, self.page);
});
}
} else {
ops.cache = ops.preload = false;
}
self.set(ops.totalCount, ops.pageSize || ops.pageCount || 10).setData(ops.data).go(ops.page);
}
factory(DataPager).extend({
//设置总的数据条数和每页显示的数据条数
set: function (totalCount, pageSize) {
var self = this;
if (totalCount != undefined) self.totalCount = totalCount;
if (pageSize != undefined) self.pageSize = pageSize;
if (self.totalCount != undefined) self.totalPage = Math.ceil(self.totalCount / self.pageSize);
//重置页码
//self.page = undefined;
return self;
},
//设置数据列表(用于ops.load)
//data:数据列表
setData: function (data) {
if (data) {
this.data = data;
this.set(data.length);
}
return this;
},
//加载数据
_load: function (page, callback) {
var self = this,
ops = self.ops,
onload = self.onload;
self.page = page;
var complete = function (list) {
if (onload) onload.call(self, list);
if (callback) callback.call(self, list);
return self;
};
//本地数据分页
if (!ops.load) {
var pageSize = self.pageSize,
start = (page - 1) * pageSize;
return complete(self.data.slice(start, start + pageSize));
}
//使用缓存
var list = ops.cache ? self.cache[page] : undefined;
if (list) return complete(list);
//获取远程数据
ops.load(page, function (data) {
list = data.data || [];
if (ops.cache) self.cache[page] = list;
self.set(data.totalCount, data.pageSize || data.pageCount);
complete(list);
});
return self;
},
//加载并渲染数据
load: function (page) {
return this._load(page, this.draw);
},
//重新载入当前数据并渲染
reload: function (page) {
return this.load(page || this.page);
},
//跳转到指定页
//forced:是否强制跳转
go: function (page, forced) {
var self = this;
if (isNaN(page)) return self;
page = +page;
if (self.totalPage != undefined && page > self.totalPage) page = self.totalPage;
if (page < 1) page = 1;
if (page == self.page && !forced) return self;
self.load(page);
if (self.ops.load && self.ops.preload) self._load(page + 1);
if (self.onchange) self.onchange.call(self, page);
return self;
},
//绘制导航区(分页按钮)
drawNav: function () {
var self = this,
ops = self.ops,
boxNav = ops.boxNav;
if (!boxNav) return self;
var totalCount = self.totalCount,
pageSize = self.pageSize,
totalPage = self.totalPage,
page = self.page,
href = ops.href,
text = ops.text || {},
list_bar = create_pager_bar_data(self.size, totalPage, page);
if (href != undefined) href += href.indexOf("?") != -1 ? "&" : "?";
var get_html_bar = function (i, title, className) {
if (!className) className = i == page ? "on" : (i ? "" : "skip");
return '<li' + (className ? ' class="' + className + '"' : '') + ' x="' + i + '">' + (href ? '<a' + (i ? ' href="' + href + 'page=' + i + '"' : '') + '>' + title + '</a>' : title) + '</li>';
};
var draw_size = ops.drawSize || function (self, html_page, html_count) {
return html_page + '/' + html_count;
};
var html =
'<div class="inline-block pager-bar' + (href ? ' pager-link' : '') + '">' +
'<ul>' +
get_html_bar(Math.max(page - 1, 1), text.prev || LANG.prevPage, "prev") +
list_bar.map(function (i) {
return get_html_bar(i, i || "…");
}).join('') +
get_html_bar(Math.min(page + 1, totalPage), text.next || LANG.nextPage, "next") +
'</ul>' +
'</div>' +
(ops.showSize !== false ?
'<div class="inline-block pager-count">' +
draw_size(self, LANG.pageSize.replace("{0}", '<span>' + pageSize + '</span>'), LANG.totalCount.replace("{0}", '<span>' + totalCount + '</span>')) +
'</div>' : '');
$(boxNav).html(html);
},
//绘制UI
draw: function (list) {
this.drawNav();
this.ops.draw.call(this, list);
return this;
}
});
//------------------------- export -------------------------
DataPager.setLang = setLang;
Q.DataPager = DataPager;
})();
/*
* Q.UI.Tabs.js 选项卡插件
* author:devin87@qq.com
* update:2020/03/26 15:48
*/
(function () {
"use strict";
var async = Q.async,
getFirst = Q.getFirst,
parseHash = Q.parseHash,
$$ = $.find;
//选项卡对象
function Tabs(ops) {
ops = ops || {};
var self = this,
context = ops.context,
tabs = ops.tabs || $$(".tab-title li.tab,.tabTitle>li", context),
conts = ops.conts || $$(".tab-cont>.turn-box,.tabCont>.turn-box", context);
self.tabs = tabs;
self.conts = conts;
self.map_loaded = {};
self.map_index = {};
self.ops = ops;
//扫描index和对应的hash
tabs.forEach(function (el, i) {
//优先显示
if (el.getAttribute("x-def") == "1") ops.index = i;
var link = getFirst(el);
if (!link) return;
var hash = link.href.split("#")[1] || "", nav = parseHash(hash).nav;
if (nav) self.map_index[nav] = i;
});
//选项卡点击事件
tabs.forEach(function (el, i) {
if ($(el).hasClass("skip")) return;
$(el).click(function () {
self.showTab(i);
});
});
$(conts).hide();
//显示默认的选项卡
setTimeout(function () {
var hash = ops.hash || parseHash().nav.slice(1),
index = self.map_index[hash];
if (index == undefined) index = ops.index || 0;
//默认显示顺序 location hash -> html定义(x-def属性) -> ops.index -> 0
self.showTab(index);
}, 20);
}
Q.factory(Tabs).extend({
//获取选项卡元素
getTab: function (i) {
return this.tabs[i];
},
//获取对应的视图元素
getCont: function (i) {
return this.conts[i];
},
//该视图是否已加载过
hasLoaded: function (i) {
return !!this.map_loaded[i];
},
//显示指定索引的选项卡
showTab: function (index) {
var self = this,
ops = self.ops,
lastIndex = self.index;
if (index === lastIndex) return;
if (lastIndex !== undefined) {
var lastTab = self.getTab(lastIndex),
lastCont = self.getCont(lastIndex);
$(lastTab).removeClass("on");
$(lastCont).hide();
}
var tab = self.getTab(index),
cont = self.getCont(index),
map_loaded = self.map_loaded;
$(tab).addClass("on");
$(cont).show();
self.index = index;
//触发选项卡切换事件
var data = { index: index, tab: tab, cont: cont, loaded: map_loaded[index] };
async(self.onchange, 100, data);
if (ops.triggerTabChange !== false) async(window["onTabChange" + (ops.name ? "_" + ops.name : "")], 200, data);
if (!map_loaded[index]) map_loaded[index] = true;
}
});
//设置选项卡切换
function setTabs(ops) {
return new Tabs(ops);
}
//------------------------- export -------------------------
Q.Tabs = Tabs;
Q.setTabs = setTabs;
})();
/*
* Q.UI.Marquee.js 无缝滚动插件
* https://github.com/devin87/Q.UI.js
* author:devin87@qq.com
* update:2022/02/14 17:01
*/
(function (undefined) {
"use strict";
var fire = Q.fire,
factory = Q.factory;
//---------------------- 无缝滚动插件 ----------------------
/*
调用示例如下:
new Q.Marquee({
box: box, //顶层DOM对象,下列ul、boxControl等都基于此对象
ul: ".slide-ul", //要滚动的ul对象
boxControl: ".slide-control", //下方滚动按钮容器,可选
btnPrev: ".prev", //向左滚动按钮
btnNext: ".next", //向右滚动按钮
way: i == 2 ? "top" : "left", //滚动方向,top:向上滚动,left:向左滚动,默认left
sleep: 5000, //自动滚动的时间间隔(ms)
isSlideKeydown: true, //是否允许键盘(左右方向键)控制滚动,默认为true
isStoppedHover: true, //鼠标移上去时停止自动滚动,移出时开始自动滚动,默认为true
auto: true //是否自动滚动
});
*/
function Marquee(ops) {
var self = this,
box = ops.box;
self._$box = $(box);
self._$ul = $(ops.ul, box);
self._$control = $(ops.boxControl, box);
self._$btnPrev = $(ops.btnPrev, box);
self._$btnNext = $(ops.btnNext, box);
self.auto = !!ops.auto;
self.way = ops.way || "left";
self.speed = ops.speed || "slow";
self.sleep = ops.sleep || 5000;
self.step = ops.step || 1;
self.isSlideKeydown = ops.isSlideKeydown !== false;
self.isStoppedHover = ops.isStoppedHover !== false;
self.clsActive = ops.clsActive || "slide-on";
self.index = 0;
self.fns = ops.on || {};
self.init();
}
factory(Marquee).extend({
//初始化
init: function () {
var self = this,
fns = self.fns,
$box = self._$box,
$ul = self._$ul,
$lis = $ul.children(),
$control = self._$control,
size = $lis.length,
$controls;
if (size > 0) {
//追加首尾元素,以实现无缝滚动
$ul.prepend($lis.last()[0].cloneNode(true));
$ul.append($lis.first()[0].cloneNode(true));
}
if (size > 1) {
$control.html('<a></a>'.repeat(size));
} else {
self._$btnPrev.hide();
self._$btnNext.hide();
}
$lis = $ul.children();
$lis.each(function (i, li) {
li.style[self.way] = (i - 1) * 100 + "%";
});
$controls = $control.children();
$controls.each(function (i) {
this.x = i;
});
self._$lis = $lis;
self._$controls = $controls;
self._cssBox = $box.prop("className");
self.size = size;
var el = $lis.get(1);
$(el).addClass(self.clsActive);
self.updateControl(0);
fire(fns.init, self, 0, el);
self.start();
return self.initEvent();
},
//初始化事件
initEvent: function () {
var self = this;
if (self.size > 1) {
//点击控制区域切换滚动
self._$control.on("click", "a", function () {
self.play(this.x);
});
//向左滚动
self._$btnPrev.click(function () {
self.playPrev();
});
//向右滚动
self._$btnNext.click(function () {
self.playNext();
});
}
if (self.isSlideKeydown) {
//键盘控制滚动
$(document).keydown(function (e) {
switch (e.keyCode) {
case 37: self.playPrev(); break;
case 39: self.playNext(); break;
}
});
}
//鼠标放在banner上时暂停播放,移除后继续
if (self.auto && self.isStoppedHover) {
self._$ul.hover(function () {
self.stop();
}, function () {
self.start();
});
}
return self;
},
//更新控制按钮
updateControl: function (i) {
var self = this,
clsName = self._cssBox || "";
self._$box.prop("className", clsName + (clsName ? " " : "") + "b" + (i + 1));
self._$controls.prop("className", "").eq(i).prop("className", self.clsActive);
return self;
},
//无缝滚动(-1<=i<=size)
play: function (i) {
var self = this,
fns = self.fns,
clsActive = self.clsActive,
$ul = self._$ul,
$lis = self._$lis,
size = self.size;
if (size <= 1) return self;
self.stop();
var i_valid = i;
if (i_valid >= size) i_valid = 0;
else if (i_valid < 0) i_valid = size - 1;
var el = $lis.get(i + 1);
$lis.removeClass(clsActive);
$(el).addClass(clsActive);
self.updateControl(i_valid);
fire(fns.beforeSlide, self, i_valid, el);
fire(self.onPlay, self, i_valid);
var params = {};
params[self.way] = (-i * 100) + "%";
$ul.animate(params, self.speed, function () {
if (i == size || i == -1) {
$lis.removeClass(clsActive).eq(i_valid + 1).addClass(clsActive);
$ul.css(self.way, (-i_valid * 100) + "%");
self.index = i_valid;
fire(self.onPlayed, self, i_valid);
}
fire(fns.slide, self, i_valid, el);
self.start();
});
self.index = i;
return self;
},
//向前滚动
playPrev: function () {
return this.play(this.index - this.step);
},
//向后滚动
playNext: function () {
return this.play(this.index + this.step);
},
//开始自动滚动
start: function () {
var self = this;
self.stop();
if (!self.auto || self.size <= 1) return self;
self.timer = setTimeout(function () {
self.playNext();
}, self.sleep);
return self;
},
//停止自动滚动
stop: function () {
var self = this;
if (self.timer) {
clearTimeout(self.timer);
self.timer = undefined;
}
return self;
}
});
//------------------------- export -------------------------
Q.Marquee = Marquee;
})();
/*
* Q.UI.ColorPicker.js 颜色选择器
* https://github.com/devin87/Q.UI.js
* author:devin87@qq.com
* update:2020/11/20 15:04
*/
(function (undefined) {
"use strict";
var document = window.document,
isFunc = Q.isFunc,
extend = Q.extend,
//fire = Q.fire,
getFirst = Q.getFirst,
getNext = Q.getNext,
getLast = Q.getLast,
//getOffset = Q.offset,
createEle = Q.createEle,
factory = Q.factory,
E = Q.event;
var LANG = {
cubic_color: "立方色",
series_color: "连续色调",
gray_color: "灰度等级"
};
//配置语言
function setLang(langs) {
extend(LANG, langs, true);
}
var POS_VALUE_HIDDEN = -10000;
//设置元素位置
function set_pos(el, x, y) {
if (x != undefined) el.style.left = x + "px";
if (y != undefined) el.style.top = y + "px";
}
//---------------------- util ----------------------
//int值转为16进制颜色
function int2hex(n, a) {
return "#" + (a ? Math.round(a * 255).toString(16) : "") + ("00000" + n.toString(16)).slice(-6); //faster
}
//RGB颜色转为16进制颜色
function rgb2hex(r, g, b, a) {
return int2hex(r * 65536 + g * 256 + b * 1, a);
}
//解析为rgba数组,若解析失败,则返回空数组 => [r,g,b,a]
//支持 #ffffff|#80ffffff|rgb(0,0,0)|rgba(0,0,0,0.5)
function parseColor(color) {
var rgba = [];
if (typeof color == "number") color = int2hex(color);
if (color.indexOf("#") == 0) {
var len = color.length;
if (len == 4 || len == 5) {
color = "#" + color.charAt(1) + color.charAt(1) + color.charAt(2) + color.charAt(2) + color.charAt(3) + color.charAt(3);
if (len == 5) color += color.charAt(4) + color.charAt(4);
}
if (color.length == 9) {
rgba[3] = Math.round(parseInt(color.substr(1, 2), 16) * 100 / 255) / 100;
color = "#" + color.slice(3);
}
if (color.length == 7) {
rgba[0] = parseInt(color.substr(1, 2), 16);
rgba[1] = parseInt(color.substr(3, 2), 16);
rgba[2] = parseInt(color.substr(5, 2), 16);
}
return rgba;
}
color = color.replace(/\s+/g, "");
var start = color.indexOf('(');
if (start != -1) {
rgba = color.slice(start + 1, -1).split(',');
}
return rgba;
}
//转为16进制颜色 eg: rgb(153,204,0) => #99CC00
function toHex(color) {
var rgba = parseColor(color);
if (rgba.length <= 0) return color;
return rgb2hex.apply(undefined, rgba.slice(0, 3));
}
//转为16进制颜色 eg: rgba(153,204,0,0.5) => #8099CC00
function toAHex(color) {
var rgba = parseColor(color);
if (rgba.length <= 0) return color;
return rgb2hex.apply(undefined, rgba);
}
//转为RGB颜色,转换失败则返回原颜色 eg: #99CC00 => rgba(153,204,0)
function toRGB(color) {
var rgba = parseColor(color);
if (rgba.length <= 0) return color;
return "rgb(" + rgba.slice(0, 3).join(",") + ")";
}
//转为RGBA颜色,转换失败则返回原颜色 eg: #8099CC00 => rgba(153,204,0,0.5)
function toRGBA(color) {
var rgba = parseColor(color);
if (rgba.length <= 0) return color;
return "rgba(" + rgba.join(",") + ")";
}
Q.parseColor = parseColor;
Q.toHex = toHex;
Q.toAHex = toAHex;
Q.toRGB = toRGB;
Q.toRGBA = toRGBA;
//---------------------- ColorPicker ----------------------
var LIST_COLOR = ['#000000', '#333333', '#666666', '#999999', '#cccccc', '#ffffff', '#ff0000', '#00ff00', '#0000ff', '#ffff00', '#00ffff', '#ff00ff'];
//颜色选择器
function ColorPicker(ops) {
var self = this;
//默认行数与列数
self.row = 12; //不得超过 LIST_COLOR.length
self.col = 21;
self.set(ops).init();
}
factory(ColorPicker).extend({
//初始化
init: function () {
var self = this;
var html =
'<div>' +
'<div></div>' +
'<div></div>' +
'<div>' +
'<select>' +
'<option value="Cube" selected="selected">' + LANG.cubic_color + '</option>' +
'<option value="Series">' + LANG.series_color + '</option>' +
'<option value="Gray">' + LANG.gray_color + '</option>' +
'</select>' +
'</div>' +
'</div>' +
'<div>' +
'<table>' +
('<tr>' + '<td></td>'.repeat(self.col) + '</tr>').repeat(self.row) +
'</table>' +
'</div>';
var box = createEle("div", "x-picker", html),
boxTitle = getFirst(box),
boxPreview = getFirst(boxTitle),
boxValue = getNext(boxPreview),
boxType = getLast(boxTitle),
boxTable = getLast(box),
table = getFirst(boxTable);
Q.body.appendChild(box);
self.box = box;
self.table = table;
self.boxPreview = boxPreview;
self.boxValue = boxValue;
//------------------- init event -------------------
//类型切换
E.add(getFirst(boxType), "change", function () {
self["draw" + this.value + "Color"]();
});
E.add(table, {
//鼠标移动,预览颜色
mouseover: function (e) {
self.setPreview(this.style.backgroundColor);
},
//选择并设置颜色
click: function (e) {
var color = this.style.backgroundColor;
self.fire(color).hide();
}
}, "td");
E.add(box, "click", E.stop);
E.add(document, "click", function (e) { self.hide(); });
return self.drawCubeColor().hide();
},
//设置 { isHex,callback }
//isHex:输出为16进制颜色
set: function (ops) {
extend(this, ops, true);
return this.setPreview((ops || {}).color);
},
//触发回调函数
fire: function (color) {
var self = this;
if (isFunc(self.callback)) self.callback.call(self, self.isHex ? toHex(color) : color);
return self;
},
//设置预览颜色
setPreview: function (color) {
var self = this;
if (color) {
self.boxPreview.style.backgroundColor = color;
self.boxValue.innerHTML = toHex(color).toUpperCase();
}
return self;
},
//填充单元格颜色
fillColor: function (i, j, color) {
this.table.rows[i].cells[j].style.backgroundColor = color;
return this;
},
//画左边的颜色
drawLeftColor: function () {
var self = this,
row = self.row;
for (var i = 0; i < row; i++) {
self.fillColor(i, 0, "#000").fillColor(i, 1, LIST_COLOR[i]).fillColor(i, 2, "#000");
}
return self;
},
//画立方色
drawCubeColor: function () {
var self = this,
row = self.row,
col = self.col,
start = 0,
step = 0x330000,
color;
self.drawLeftColor();
for (var i = 0; i < row; i++) {
if (i > 5) color = start = 0x990000 + (i - 6) * 0x000033;
else color = start = 0x0 + i * 0x000033;
for (var j = 3; j < col; j++) {
self.fillColor(i, j, int2hex(color));
color += 0x003300;
if ((j - 2) % 6 == 0) start += step, color = start;
}
}
return self;
},
//画连续色
drawSeriesColor: function () {
var self = this,
row = self.row,
col = self.col,
start = 0xCCFFFF,
step = 0x660000,
flag = 1,
color;
self.drawLeftColor();
for (var i = 0; i < row; i++) {
if (i > 5) color = start = 0xFF00FF + (i - 6) * 0x003300;
else color = start = 0xCCFFFF - i * 0x003300;
flag = 1;
for (var j = 3; j < col; j++) {
self.fillColor(i, j, int2hex(color));
color -= 0x000033 * flag;
if ((j - 2) % 6 == 0) {
flag *= -1;
start -= step;
color = start - ((flag > 0) ? 0 : 0x0000FF);
}
}
}
return self;
},
//画灰度等级色
drawGrayColor: function () {
var self = this,
row = self.row,
col = self.col,
color = 0xffffff;
for (var i = 0; i < row; i++) {
for (var j = 0; j < col; j++) {
self.fillColor(i, j, int2hex(color));
if (color <= 0) color = 0x000000;
else color -= 0x010101;
}
}
return self;
},
//显示
show: function (x, y) {
var self = this;
if (x == undefined) x = self.x;
else self.x = x;
if (y == undefined) y = self.y;
else self.y = y;
set_pos(self.box, x, y);
return self;
},
//隐藏
hide: function () {
set_pos(this.box, POS_VALUE_HIDDEN, POS_VALUE_HIDDEN);
return this;
},
//是否为隐藏状态
isHidden: function () {
var self = this,
box = self.box,
x = parseFloat(box.style.left),
y = parseFloat(box.style.top);
return x <= -box.offsetWidth || y <= -box.offsetHeight;
},
//自动切换显示或隐藏
toggle: function (x, y) {
return this.isHidden() ? this.show(x, y) : this.hide();
}
});
ColorPicker.setLang = setLang;
//------------------------- export -------------------------
Q.ColorPicker = ColorPicker;
})();
/*
* Q.UI.Progressbar.js 进度条
* https://github.com/devin87/Q.UI.js
* author:devin87@qq.com
* update:2015/07/15 11:46
*/
(function (undefined) {
"use strict";
var fire = Q.fire,
createEle = Q.createEle,
factory = Q.factory;
//---------------------- 进度条 ----------------------
//进度条,颜色、高度等设置可以在样式表里控制
//ops:{box:进度条所在容器,speed:速度(0-100),wait:每隔多长时间更新一次进度(ms),onprogress(progress,speed,time):进度更新时回调函数}
function Progressbar(ops) {
ops = ops || {};
var container = ops.box,
boxBar = createEle("div", "progress-bar"),
boxNode = createEle("div", "progress");
boxBar.appendChild(boxNode);
container.appendChild(boxBar);
var self = this;
self._bar = boxBar;
self._node = boxNode;
self.speed = ops.speed || 1;
self.wait = ops.wait || 100;
self.progress = 0;
self.time = 0;
self.onprogress = ops.onprogress;
}
factory(Progressbar).extend({
//启动进度条
start: function () {
var self = this;
if (self.progress >= 100) return;
self._timer = setInterval(function () {
self.update();
}, self.wait);
return self;
},
//停止进度条
stop: function () {
if (this._timer) clearInterval(this._timer);
return this;
},
//重新启动进度条(进度归0)
restart: function () {
return this.stop().update(0).start();
},
//更新进度条进度
update: function (progress) {
var self = this;
if (progress != undefined) self.progress = Math.max(progress, 0);
else self.progress += self.speed;
if (self.progress > 100) {
self.progress = 100;
self.stop();
}
self._node.style.width = self.progress.toFixed(2) + "%";
self.time += self.wait;
fire(self.onprogress, self, self.progress, self.speed, self.time);
return self;
},
//设置速度和等待时间
set: function (speed, wait) {
if (speed) this.speed = speed;
if (wait) this.wait = wait;
return this.stop().start();
}
});
//------------------------- export -------------------------
Q.Progressbar = Progressbar;
})();
/*
* Q.UI.RangeSlider.js 滑动条(input[type=range])
* https://github.com/devin87/Q.UI.js
* author:devin87@qq.com
* update:2019/04/24 11:14
*/
(function (undefined) {
"use strict";
var fire = Q.fire,
createEle = Q.createEle,
factory = Q.factory,
DragX = Q.DragX;
//---------------------- 滑动条 ----------------------
//滑动条,颜色、高度等设置可以在样式表里控制
/* ops配置:
{
box: el, //滑动条容器
min: 0, //滑动条最小值
max: 100, //滑动条最大值
step: 1, //滑动步进
value: 0 //滑动条当前值
}
*/
function RangeSlider(ops) {
ops = ops || {};
var container = ops.box,
elBar = createEle("div", "range-slider-bar"),
elProgress = createEle("div", "range-progress"),
elSlider = createEle("div", "range-slider");
elBar.appendChild(elProgress);
elBar.appendChild(elSlider);
container.appendChild(elBar);
var self = this;
self._elBar = elBar;
self._elProgress = elProgress;
self._elSlider = elSlider;
self.min = +ops.min || 0;
self.max = +ops.max || 100;
self.step = +ops.step || 1;
self.value = +ops.value || 0;
var str = self.step + '',
i = str.lastIndexOf('.'),
n = i != -1 ? str.length - i - 1 : 0;
//修复step非整数时精度不准确的问题
var FIX_INT = Math.pow(10, n);
self.onchange = ops.onchange;
self.val(self.value);
self._drag = new DragX(function () {
var base = this,
totalWidth,
startWidth, //初始宽度
startX, //初始x坐标
maxLeft; //elSlider最大偏移
//实现ops接口
base.ops = { ele: elSlider, autoCursor: false };
//实现doDown接口
base.doDown = function (e) {
totalWidth = elBar.offsetWidth;
startWidth = elProgress.offsetWidth;
startX = e.clientX;
maxLeft = totalWidth - elSlider.offsetWidth;
};
//实现doMove接口
base.doMove = function (e) {
//水平移动的距离
var x = e.clientX - startX,
w = startWidth + x;
if (w < 0) w = 0;
else if (w > totalWidth) w = totalWidth;
var steps = ~~(w * (self.max - self.min) / (totalWidth * self.step)),
v = (self.min * FIX_INT + self.step * FIX_INT * steps) / FIX_INT;
elProgress.style.width = (w * 100 / totalWidth) + '%';
elSlider.style.left = (Math.min(w, maxLeft) * 100 / totalWidth) + '%';
if (v != self.value) {
self.value = v;
fire(self.onchange, self, self.value);
}
};
});
}
factory(RangeSlider).extend({
val: function (v) {
var self = this;
if (v == undefined) return self.value;
if (v < self.min) v = self.min;
else if (v > self.max) v = self.max;
var elProgress = self._elProgress,
elSlider = self._elSlider,
totalWidth = self._elBar.offsetWidth;
self.value = v;
elProgress.style.width = ((v - self.min) * 100 / (self.max - self.min)) + '%';
elSlider.style.left = (Math.min(elProgress.offsetWidth, totalWidth - elSlider.offsetWidth) * 100 / totalWidth) + '%';
return self;
}
});
//------------------------- export -------------------------
Q.RangeSlider = RangeSlider;
})();
返回顶部