Files
Pandora/Pandora.js_bak
2025-08-11 23:49:58 +08:00

3263 lines
110 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(w => {
"use strict";
const OSSBase64 = require("./src/base64.js");
const icoConfig = require("./src/icoConfig.json");
// 缺省字段
const Alphabet = ["active", "local", "localhost", "pandorajs.com", "127.0.0.1", "192.168", "inherit", "提示", "错误", "警告"];
// 弹框集合
w.pdDialogs = [];
// 兼容处理&&基础方法
//requestAnimationFrame
if (!w.requestAnimationFrame) {
let lastTime, reqId;
w.requestAnimationFrame = callback => {
const currTime = new Date().getTime(),
timeToCall = Math.max(0, 16 - (currTime - lastTime)),
reqId = setTimeout(() => {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return reqId;
};
w.cancelAnimationFrame = reqId => {
clearTimeout(reqId);
};
}
(function () {
//获取CSS变量
const getRoot = name => {
return w.getComputedStyle(document.documentElement).getPropertyValue(`--${name}`);
};
// 添加alert、confirm皮肤样式
let rootText = "";
if (!getRoot("alertTheme")) rootText += `/*alert背景*/--alertTheme:${Alphabet[6]};`;
if (!getRoot("alertBg")) rootText += `/*alert遮罩*/--alertBg:${Alphabet[6]};`;
if (!getRoot("alertFontSize")) rootText += "/*alert字体大小*/--alertFontSize:1rem;";
if (!getRoot("alertColor")) rootText += "/*alert字体颜色*/--alertColor:#000;";
if (!getRoot("confirmTheme")) rootText += `/*confirm背景*/--confirmTheme:#fff;`;
if (!getRoot("confirmBg")) rootText += `/*confirm遮罩*/--confirmBg:${Alphabet[6]};`;
if (!getRoot("confirmBtnBg")) rootText += "/*confirm按钮背景*/--confirmBtnBg:#fafafa;";
if (!getRoot("confirmFontSize")) rootText += "/*confirm字体大小*/--confirmFontSize:1rem;";
if (!getRoot("confirmColor")) rootText += "/*confirm字体颜色*/--confirmColor:#636363;";
if (!getRoot("confirmBtnColor")) rootText += "/*confirm按钮字体颜色*/--confirmBtnColor:#636363;";
if (!getRoot("confirmBtnBorder")) rootText += "/*confirm按钮边框颜色*/--confirmBtnBorder:#f1f1f1;";
const style = document.createElement("style");
style.innerText = `:root{${rootText}}`;
document.querySelector("head").appendChild(style);
//是否已经显示遮罩
let isMaskShow = !1;
const setGlobalCSS = (mask, maskBg, blur, plan) => {
mask.style.cssText = `
position:fixed;
inset:0;
top:0;
left:0;
right:0;
bottom:0;
z-index:999999999;
width:100%;
height:100%;
display:flex;
justify-content:center;
background:${Alphabet[6]};
background:${maskBg}`;
mask.className = "Pd_Mask";
blur.style.cssText = plan.style.cssText = `position:absolute;inset:0;top:0;left:0;right:0;bottom:0;`;
plan.style.cssText += `background:${Alphabet[6]};filter:blur(10px) saturate(2)`;
if (maskBg) plan.style.cssText += `background:rgba(255,255,255,.66)`;
};
//修改原生alert
if (w.resetAlert == undefined) {
w.resetAlert = !0;
}
if (w.resetAlert) {
w.alert = (content, speed = 800) => {
let timeout,
mask = document.createElement(`div`),
maskBg = !isMaskShow ? getRoot(`alertBg`) : null,
div = document.createElement(`div`),
msg = document.createElement(`p`),
blur = document.createElement(`div`),
plan = document.createElement(`div`),
Theme = getRoot(`alertTheme`),
fontSize = getRoot(`alertFontSize`),
color = getRoot(`alertColor`);
setGlobalCSS(mask, maskBg, blur, plan);
mask.style.cssText += `align-items:flex-end`;
div.style.cssText = `
background:${Alphabet[6]};
background:${Theme};
text-align:center;
color:${color};
font-size:${fontSize};
padding:.5em 1.5em;
line-height:1.3;
transition:opacity .4s ease-out;
margin-bottom:5vh;
box-shadow:0 8px 16px rgba(0,0,0,.25);
border-radius:6px;
position:relative;
overflow:hidden`;
div.id = "Pd_alert";
msg.style.cssText = `margin:0;position:relative`;
msg.innerText = content ? content + "" : "";
div.appendChild(blur);
div.appendChild(plan);
div.appendChild(msg);
mask.appendChild(div);
document.body.appendChild(mask);
mask.onclick = () => {
clearTimeout(timeout);
document.body.removeChild(mask);
mask = div = timeout = color = null;
};
clearTimeout(timeout);
timeout = setTimeout(() => {
div.style.opacity = 0;
div.addEventListener("transitionend", () => {
document.body.removeChild(mask);
mask = div = timeout = color = null;
});
}, speed);
};
}
//修改原生confirm
if (w.resetConfirm == undefined) {
w.resetConfirm = !0;
}
if (w.resetConfirm) {
w.confirm = config => {
let title = config.title || "",
content,
confirmText,
cancelText,
mask = document.createElement(`div`),
div = document.createElement(`div`),
blur = document.createElement(`div`),
plan = document.createElement(`div`),
titleCon = document.createElement(`h2`),
msg = document.createElement(`p`),
confirm = document.createElement(`button`),
cancel = document.createElement(`button`),
maskBg = !isMaskShow ? getRoot(`confirmBg`) : null,
btnBg = getRoot(`confirmBtnBg`),
Theme = getRoot(`confirmTheme`),
fontSize = getRoot(`confirmFontSize`),
color = getRoot(`confirmColor`),
btnColor = getRoot(`confirmBtnColor`),
btnBorder = getRoot(`confirmBtnBorder`);
const showConfirm = config.showConfirm == undefined ? !0 : config.showConfirm,
showCancel = config.showCancel == undefined ? !0 : config.showCancel;
confirmText = config.confirmText ? config.confirmText.toString() : "确认";
cancelText = config.cancelText ? config.cancelText.toString() : "取消";
setGlobalCSS(mask, maskBg, blur, plan);
mask.style.cssText += "align-items:center;";
div.style.cssText = `
background:${Alphabet[6]};
background:${Theme};
text-align:center;
color:${color};
font-size:${fontSize};
max-width:75vw;
min-width:20em;
box-shadow:0px 35px 35px -10px rgba(0,0,0,.33);
border-radius:10px;
position:relative;
white-space: break-spaces;
word-break: break-all;
overflow:hidden`;
div.id = "Pd_confirm";
titleCon.style.cssText = `position:relative;font-size:1.3em;min-height:1em;background:${btnBg};color:${btnColor};margin:0;padding:.5em 0`;
msg.style.cssText = `position:relative;border-top:1px solid ${btnBorder};width:100%;max-height:70vh;border-bottom:1px solid ${btnBorder};margin:0 auto;box-sizing:border-box;padding:2em 10%;line-height:1.4;overflow:auto`;
const buttonCSS = `position: relative;width:${
showConfirm && showCancel ? "50%" : "100%"
};font-size:1em;appearance:none;background:${btnBg};color:${btnColor};border:none;border-right:1px solid ${btnBorder};padding:1em 0;cursor:pointer;outline:none`;
confirm.style.cssText = cancel.style.cssText = buttonCSS;
const removeConfirm = () => {
document.body.removeChild(mask);
isMaskShow = !1;
};
if (!config.content) {
content = config + "";
} else {
content = config.content.toString();
}
titleCon.innerText = title ? title.toString() : "";
msg.innerText = content ? content.toString() : "";
div.appendChild(blur);
div.appendChild(plan);
div.appendChild(titleCon);
div.appendChild(msg);
if (showConfirm) {
confirm.innerText = confirmText;
div.appendChild(confirm);
}
if (showCancel) {
cancel.innerText = cancelText;
div.appendChild(cancel);
}
mask.appendChild(div);
document.body.appendChild(mask);
isMaskShow = !0;
return new Promise((ok, no) => {
if (showConfirm) {
confirm.onclick = () => {
removeConfirm();
ok();
};
}
if (showCancel) {
cancel.onclick = () => {
removeConfirm();
no();
};
}
});
};
}
//loading遮罩
const LoadingName = "Pd_loader";
let mask;
w.showLoading = (progress = null) => {
if (document.getElementById(LoadingName)) {
document.getElementById(LoadingName) && document.body.removeChild(document.getElementById(LoadingName));
}
mask = document.createElement("div");
const svg = new Image(),
em = document.createElement("em");
svg.src = icoConfig.load;
mask.id = LoadingName;
mask.style.cssText =
"font-size:1rem;width:100%;height:100%;position:fixed;z-index:999999999;inset:0;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.65);display:flex;align-items:center;justify-content:center;flex-direction:column";
svg.style.width = svg.style.height = "3em";
mask.appendChild(svg);
if (progress !== null) {
em.style.cssText = "font-style:normal;font-size:1em;color:#fff;margin-top:.5em;";
em.innerText = progress;
mask.appendChild(em);
}
document.body.appendChild(mask);
};
w.hideLoading = () => {
if (document.getElementById(LoadingName)) {
document.body.removeChild(mask);
}
};
})();
//内置方法
class PandoraEX {
constructor(input = null) {
this.getInput = obj => {
if (Array.isArray(obj)) return obj;
if (["[object Window]", "[object HTMLDocument]"].includes(obj + "")) return w;
if (document.querySelectorAll(obj)) {
if (document.querySelectorAll(obj).length > 1) {
return document.querySelectorAll(obj);
} else {
if (obj) {
return document.querySelector(obj);
} else {
return null;
}
}
} else {
return console.error(`[${Alphabet[8]}] 未找到 ${obj}`);
}
};
this.get = this.getInput(input);
this.getLength = () => {
if (this.get) {
const { length } = this.get;
if (length) {
return length;
} else {
return 1;
}
}
};
this.length = this.getLength();
this.guid = () => {
const S4 = () => {
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
};
return `PandoraEX_${S4()}${S4()}-${S4()}-${S4()}-${S4()}-${S4()}${S4()}${S4()}`;
};
//生成GUID
this.pid = this.guid();
//默认参数赋值
this.extend = (config, options) => {
if (!options) {
options = config;
} else {
Object.keys(config).forEach(e => {
if (typeof options[e] === undefined || typeof options[e] === undefined + "") options[e] = config[e];
});
}
return options;
};
//选择指定下标元素
this.eq = index => {
try {
if (this.getInput(input).length) {
this.get = this.getInput(input)[index];
} else {
this.get = this.getInput(input);
}
} catch (err) {
console.error(`[${Alphabet[8]}] 未找到该下标`, err);
}
return this;
};
//选择子级元素
this.child = name => {
const ele = this.get;
try {
if (ele.querySelectorAll(name).length > 1) {
this.get = ele.querySelectorAll(name);
} else {
this.get = ele.querySelectorAll(name)[0];
}
} catch (err) {
console.error(`[${Alphabet[8]}] 未找到该子级`, err);
}
return this;
};
//查找子级元素
this.find = name => {
const ele = this.get;
try {
this.get = ele.querySelectorAll(name);
} catch (err) {
console.error(`[${Alphabet[8]}] 未找到该子级`, err);
}
return this;
};
//选择父级
this.parent = () => {
const ele = this.get;
try {
this.get = ele.parentElement;
} catch (err) {
console.error(`[${Alphabet[8]}] 未找到该父级`, err);
}
return this;
};
//选择其他同级元素
this.siblings = name => {
const ele = this.get,
parent = this.parent();
let siblingArr = [];
for (let e of parent.child(name).get) {
if (ele != e) siblingArr.push(e);
}
this.get = siblingArr;
return this;
};
//选择上一个同级元素
this.prev = () => {
const ele = this.get;
this.get = ele.previousElementSibling;
return this;
};
//选择下一个同级元素
this.next = () => {
const ele = this.get;
this.get = ele.nextElementSibling;
return this;
};
//选择第一个元素
this.first = () => {
return this.eq(0);
};
//选择最后一个元素
this.last = () => {
const ele = this.get;
if (ele.length) {
return this.eq(ele.length - 1);
} else {
return this.first();
}
};
//遍历元素集
this.each = fn => {
const ele = this.get;
if (ele.length) {
for (let i = 0; i < ele.length; i++) {
fn && fn(this.eq(i), i);
}
} else {
fn && fn(this.first(), 0);
}
return this;
};
//获取或修改样式
this.css = name => {
const ele = this.get;
let style = [];
style = name;
if (style) {
if (typeof style === "string") {
if (ele.length) {
return w.getComputedStyle(ele[0]).getPropertyValue(style);
} else {
return w.getComputedStyle(ele).getPropertyValue(style);
}
} else {
if (ele.length) {
for (let the of ele) {
Object.keys(style).forEach(e => {
the.style[e] = style[e];
});
}
} else {
Object.keys(style).forEach(e => {
ele.style[e] = style[e];
});
}
}
} else {
return w.getComputedStyle(ele).getPropertyValue("*");
}
};
//获取布局信息
this.offset = () => {
const ele = this.get;
if (ele.length) {
return ele[0].getBoundingClientRect();
} else {
return ele.getBoundingClientRect();
}
};
//获取或设置宽度
this.width = (width = null) => {
const ele = this.get;
if (width) {
if (ele.length) {
for (let the of ele) the.style.width = width;
} else {
ele.style.width = width;
}
} else {
if (ele.length) {
return ele[0].offsetWidth;
} else {
return ele.offsetWidth;
}
}
};
//获取或设置高度
this.height = (height = null) => {
const ele = this.get;
if (height) {
if (ele.length) {
for (let the of ele) the.style.height = height;
} else {
ele.style.height = height;
}
} else {
if (ele.length) {
return ele[0].offsetHeight;
} else {
return ele.offsetHeight;
}
}
};
//获取或插入文本
this.text = str => {
const ele = this.get;
if (str != undefined) {
if (ele.length) {
for (let the of ele) the.innerText = str + "";
} else {
ele.innerText = str + "";
}
} else {
if (ele.length) {
return ele[0].innerText;
} else {
return ele.innerText;
}
}
return this;
};
//获取或插入HTML
this.html = content => {
const ele = this.get;
if (content) {
this.empty();
if (ele.length) {
for (let the of ele) the.innerHTML = content;
} else {
ele.innerHTML = content;
}
} else {
if (ele.length) {
return ele[0].innerHTML;
} else {
return ele.innerHTML;
}
}
return this;
};
//获取或插入值
this.val = value => {
const ele = this.get;
if (ele.nodeName.toLowerCase() == "select") {
if (value != null && value != undefined) {
for (let the of ele) the.options[the.selectedIndex].value = value;
} else {
return ele.options[ele.selectedIndex].value;
}
} else {
if (ele.length) {
if (value != null && value != undefined) {
for (let the of ele) the.value = value;
} else {
return ele[0].value;
}
} else {
if (value != null && value != undefined) {
ele.value = value;
} else {
return ele.value;
}
}
}
return this;
};
//插入元素
this.prepend = target => {
const ele = this.get;
if (ele.length > 1) {
if (ele.nodeName.toLowerCase() == "select") {
if (typeof target == "object") {
ele.insertBefore(target, ele.firstChild);
} else {
const div = document.createElement("div");
div.innerHTML = target;
for (const dom of div.childNodes) ele.insertBefore(dom, ele.firstChild);
}
} else {
for (let the of ele) {
if (typeof target == "object") {
the.insertBefore(target, the.firstChild);
} else {
const div = document.createElement("div");
div.innerHTML = target;
for (const dom of div.childNodes) the.insertBefore(dom, the.firstChild);
}
}
}
} else {
if (typeof target == "object") {
ele.insertBefore(target, ele.firstChild);
} else {
const div = document.createElement("div");
div.innerHTML = target;
for (const dom of div.childNodes) ele.insertBefore(dom, ele.firstChild);
}
}
return this;
};
this.append = target => {
const ele = this.get;
if (ele.length > 1) {
if (ele.nodeName.toLowerCase() == "select") {
if (typeof target == "object") {
ele.appendChild(target);
} else {
const template = document.createElement("template");
template.innerHTML = target;
ele.appendChild(document.importNode(template.content, !0));
}
} else {
for (let the of ele) {
if (typeof target == "object") {
the.appendChild(target);
} else {
const template = document.createElement("template");
template.innerHTML = target;
the.appendChild(document.importNode(template.content, !0));
}
}
}
} else {
if (typeof target == "object") {
ele.appendChild(target);
} else {
const template = document.createElement("template");
template.innerHTML = target;
ele.appendChild(document.importNode(template.content, !0));
}
}
return this;
};
//清空容器
this.empty = () => {
const ele = this.get;
if (ele.length) {
for (let the of ele) {
while (the.firstChild) {
the.removeChild(the.firstChild);
}
}
} else {
while (ele.firstChild) {
ele.removeChild(ele.firstChild);
}
}
return this;
};
//移除元素
this.remove = () => {
const ele = this.get;
if (ele.length) {
for (let the of ele) {
try {
the.parentElement.removeChild(the);
} catch (err) {
console.error(`[${Alphabet[8]}] 未找到元素`, err);
}
}
} else {
try {
ele.parentElement.removeChild(ele);
} catch (err) {
console.error(`[${Alphabet[8]}] 未找到元素`, err);
}
}
return this;
};
//添加class
this.addClass = name => {
const ele = this.get,
addThat = the => {
const beforeClass = the.classList.value;
if (beforeClass) {
if (beforeClass.indexOf(name) < 0) the.className = `${beforeClass} ${name.trim()}`;
} else {
the.className = name.trim();
}
};
if (ele.length) {
for (let the of ele) addThat(the);
} else {
addThat(ele);
}
return this;
};
//移除class
this.removeClass = name => {
const ele = this.get,
removeThat = the => {
if (the.classList.value) {
let beforeClass = the.classList.value.split(" "),
afterClass;
beforeClass.map((cur, idx) => {
if (cur === name) beforeClass.splice(idx, 1);
});
afterClass = beforeClass.join(" ");
the.className = afterClass;
}
};
if (ele.length) {
for (let the of ele) removeThat(the);
} else {
removeThat(ele);
}
return this;
};
//是否拥有class名
this.hasClass = name => {
const ele = this.get,
classlist = ele.classList.value.indexOf(" ") > -1 ? ele.classList.value.split(" ") : ele.classList.value;
if (classlist.indexOf(name) > -1) {
return !0;
} else {
return !1;
}
};
//添加属性
this.attr = (inject, val) => {
const ele = this.get;
if (ele.length) {
if (typeof inject == "object") {
for (let the of ele) {
for (let keyName in inject) the.setAttribute(keyName, inject[keyName]);
}
return this;
} else {
if (val) {
for (let the of ele) the.setAttribute(inject, val);
return this;
} else {
return ele[0].getAttribute(inject);
}
}
} else {
if (typeof inject == "object") {
for (let keyName in inject) ele.setAttribute(keyName, inject[keyName]);
return this;
} else {
if (val) {
ele.setAttribute(inject, val);
return this;
} else {
return ele.getAttribute(inject);
}
}
}
};
//移除属性
this.removeAttr = name => {
const ele = this.get;
if (ele.length) {
for (let the of ele) the.removeAttribute(name);
} else {
ele.removeAttribute(name);
}
return this;
};
//绑定事件
this.bind = (eventName, fn, options = {}, bool = !1) => {
const ele = this.get;
const capture = options.capture || !1,
once = options.once || !1,
passive = options.passive || !1;
if (ele.length) {
let eleIndex = 0;
for (let the of ele) {
the.addEventListener(eventName, fn, { capture, once, passive }, bool);
the.index = eleIndex++;
the.eventList = [];
the.eventList.push({ name: eventName, callback: fn });
}
} else {
ele.addEventListener(eventName, fn, { capture, once, passive }, bool);
ele.eventList = [];
ele.eventList.push({ name: eventName, callback: fn });
}
return this;
};
//解绑事件
this.unbind = eventName => {
const ele = this.get;
if (ele.length) {
for (let the of ele) {
if (the.eventList) {
the.eventList.map((e, i) => {
if (e.name === eventName) {
the.removeEventListener(eventName, e.callback);
the.eventList.splice(i, 1);
}
});
}
}
} else {
if (ele.eventList) {
ele.eventList.map((e, i) => {
if (e.name === eventName) {
ele.removeEventListener(eventName, e.callback);
ele.eventList.splice(i, 1);
}
});
}
}
return this;
};
//获取焦点
this.focus = () => {
const ele = this.get;
if (ele.length) {
for (let the of ele) {
the.focus();
}
} else {
ele.focus();
}
return this;
};
//移除焦点
this.blur = () => {
const ele = this.get;
if (ele.length) {
for (let the of ele) {
the.blur();
}
} else {
ele.blur();
}
return this;
};
//点击&触摸点击事件
this.click = fn => {
const ele = this.get;
if (ele.length) {
for (let a = 0; a < ele.length; a++) {
if (w.ontouchstart) {
ele[a].ontouchstart = () => {
this.get = ele[a];
fn(this, a);
};
} else {
ele[a].onclick = () => {
this.get = ele[a];
fn(this, a);
};
}
}
} else {
if (w.ontouchstart) {
ele.ontouchstart = () => {
fn(this, null);
};
} else {
ele.onclick = () => {
fn(this, null);
};
}
}
return this;
};
//双击事件
this.dblclick = fn => {
const ele = this.get;
if (ele.length) {
for (let a = 0; a < ele.length; a++) {
ele[a].ondblclick = () => {
this.get = ele[a];
fn(this, a);
};
}
} else {
ele.ondblclick = () => {
fn(this, null);
};
}
return this;
};
//主动触发事件
this.trigger = eventName => {
const ele = this.get;
const event = new Event(eventName);
if (ele.length) {
for (let the of ele) {
the.dispatchEvent(event);
}
} else {
ele.dispatchEvent(event);
}
return this;
};
//长按事件
this.taping = (fn, cb) => {
const ele = this.get;
let infiniteFrame;
const infiniteFn = () => {
fn && fn(ele);
infiniteFrame = requestAnimationFrame(infiniteFn);
};
if (w.ontouchstart) {
ele.ontouchstart = event => {
event.preventDefault();
cancelAnimationFrame(infiniteFn);
infiniteFn();
};
ele.ontouchend = () => {
cb && cb(ele);
cancelAnimationFrame(infiniteFrame);
};
} else {
ele.onmousedown = () => {
cancelAnimationFrame(infiniteFn);
infiniteFn();
};
ele.onmouseup = () => {
cb && cb(ele);
cancelAnimationFrame(infiniteFrame);
};
}
return this;
};
//显示
this.show = callback => {
this.attr(`beforeHide`) ? this.css({ display: this.attr(`beforeHide`) }) : this.css({ display: "block" });
callback && setTimeout(callback);
return this;
};
//隐藏
this.hide = callback => {
if (!this.attr(`beforeHide`)) this.attr("beforeHide", this.css(`display`) == "none" ? "block" : this.css(`display`));
this.css({ display: "none" });
callback && setTimeout(callback);
return this;
};
// 淡入
this.fadeIn = (speed = "fast", callback) => {
const that = this;
let opacity = 0,
req;
const fade = () => {
if (opacity < 100) {
switch (speed) {
case "fast":
opacity += 5;
break;
case "slow":
opacity++;
break;
default:
opacity += speed;
break;
}
req = requestAnimationFrame(fade);
} else {
callback && callback();
cancelAnimationFrame(req);
}
that.css({ opacity: opacity / 100 });
};
that.show(() => {
that.css({ opacity: 0 });
fade();
});
return this;
};
// 淡出
this.fadeOut = (speed = "fast", callback) => {
const that = this;
let opacity = 100,
req;
const fade = () => {
if (opacity > 0) {
switch (speed) {
case "fast":
opacity -= 5;
break;
case "slow":
opacity--;
break;
default:
opacity -= speed;
break;
}
req = requestAnimationFrame(fade);
} else {
that.hide(() => {
callback && callback();
cancelAnimationFrame(req);
});
}
that.css({ opacity: opacity / 100 });
};
fade();
return this;
};
//ajax
this.ajax = options => {
let config = {
//接口地址(类型:字符串)
url: null,
//请求类型(类型字符串可选参数post、get)
type: "get",
//是否同步请求(类型:布尔)
async: !1,
//设置请求头(类型:对象)
headers: { "Content-type": "application/x-www-form-urlencoded" },
//发送数据类型(类型字符串可选参数json、form)
dataType: "json",
//返回体类型(类型:字符串)
responseType: "json",
//发送数据(类型json或form格式必须和发送数据类型保持一致)
data: null,
//加载中回调方法(类型:方法)
progress: null,
//成功回调方法(类型:方法)
success: null,
//失败回调方法(类型:方法)
error: null,
};
config = this.extend(config, options);
const http = new XMLHttpRequest(),
{ url, type, async, headers, dataType, data, progress, success, error, responseType } = config;
let params;
if (dataType == "json") {
if (data)
params = Object.keys(data)
.map(key => {
return `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`;
})
.join(`&`);
} else {
params = data;
}
if (async) {
http.responseType = responseType;
}
try {
// 监听加载中
http.upload.onprogress = event => {
if (event.lengthComputable) {
progress && progress(Math.floor((event.loaded / event.total) * 100));
}
};
} catch (e) {}
// 向全局添加取消请求方法
w.cancelAjax = () => {
http.abort();
};
return new Promise((resolve, reject) => {
http.onreadystatechange = () => {
if (http.status === 200 && http.readyState === 4 && http.response) {
if (async) {
resolve(http.response);
success && success(http.response);
} else {
resolve(JSON.parse(http.response));
success && success(JSON.parse(http.response));
}
}
};
http.onerror = err => {
reject(err);
error && error(err);
};
http.open(type.toUpperCase(), url, async);
if (headers) {
// 如果data是formdata类型不设置请求头
if (data && data.constructor !== FormData) {
for (let keys in headers) http.setRequestHeader(keys, headers[keys]);
}
}
http.send(params);
});
};
//fetch
this.fetch = options => {
let config = {
//接口地址(类型:字符串)
url: null,
//设置请求头(类型:对象)
headers: { "Content-type": "application/x-www-form-urlencoded" },
//请求类型(类型字符串可选参数post、get)
type: "get",
//发送数据(类型JSON)
data: null,
//返回数据格式化(类型:方法)
returnData(res) {
return res.json();
},
};
config = this.extend(config, options);
const { url, data, headers, type, returnData } = config;
return new Promise((resolve, reject) => {
if (data) {
fetch(url, { body: JSON.stringify(data), headers, method: type.toLocaleUpperCase() })
.then(res => {
if (res.ok) {
return returnData(res);
} else {
console.error(`[${Alphabet[8]}] 请求错误`, res);
}
})
.then(resolve)
.catch(reject);
} else {
fetch(url, { headers, method: type.toLocaleUpperCase() })
.then(res => {
if (res.ok) {
return returnData(res);
} else {
console.error(`[${Alphabet[8]}] 请求错误`, res);
}
})
.then(resolve)
.catch(reject);
}
});
};
//表单序列化
this.serialize = () => {
const ele = this.get;
let obj = {};
for (let e of ele.querySelectorAll(`*`)) {
if (e.getAttribute(`name`)) {
const keyName = e.getAttribute(`name`);
if (keyName) {
switch (e.type) {
case "radio":
if (e.checked) obj[keyName] = e.value;
break;
case "checkbox":
if (e.checked) {
obj[keyName] = e.value;
} else {
obj[keyName] = !1;
}
break;
default:
obj[keyName] = e.value;
break;
}
}
}
}
return obj;
};
//NEW设置表单
this.setForm = (obj = null) => {
if (obj) {
const ele = this.get,
formChilds = ele.querySelectorAll(`[name]`);
formChilds.forEach(e => {
const keyName = e.getAttribute(`name`);
if (obj[keyName]) {
if (e.readOnly) return console.error(`[${Alphabet[8]}] 该元素为只读,无法设置值`);
switch (e.type) {
case "radio":
if (e.value == obj[keyName]) {
e.checked = !0;
} else {
e.checked = !1;
}
break;
case "checkbox":
if (obj[keyName]) e.checked = !0;
break;
case "file":
e.files = obj[keyName];
break;
case "select":
e.value = obj[keyName];
break;
default:
e.value = obj[keyName];
break;
}
}
});
} else {
console.error(`[${Alphabet[8]}] 无可设置的表单数据`);
}
return this;
};
//渲染变量
this.globalData = {};
//设置渲染变量
this.setData = obj => {
return new Promise((success, fail) => {
for (let key in obj) {
try {
this.globalData[key] = obj[key];
} catch (err) {
console.error(`[${Alphabet[8]} - Mush] 变量修改失败`, err);
fail(err);
}
}
success();
});
};
//获取渲染变量
this.getData = key => {
if (this.globalData[key]) {
return this.globalData[key];
} else {
console.error(`[${Alphabet[8]} - Mush] 获取的变量不存在!`);
return null;
}
};
//模板渲染
this.template = (route, container) => {
return new Promise((success, fail) => {
const temp = (() => {
let cur;
const template = document.querySelectorAll(`template`);
for (let a = 0; a < template.length; a++) {
if (template[a].getAttribute(`route`) == route) cur = template[a];
}
return cur;
})();
if (temp) {
this.empty();
let url = temp.getAttribute("src");
const that = this;
if (url) {
that
.fetch({
url,
headers: { "Content-type": "text/html" },
returnData(res) {
return res.text();
},
})
.then(res => {
const node = document.createElement(`template`);
node.innerHTML = res;
if (node.content.querySelectorAll(`link`)) {
let linkArr = [];
for (let link of node.content.querySelectorAll(`link`)) {
linkArr.push(
that.fetch({
url: link.href,
headers: { "Content-type": "text/html" },
returnData(res) {
return res.text();
},
})
);
node.content.removeChild(link);
}
Promise.all(linkArr).then(res => {
res.map(the => {
const style = document.createElement("style");
style.innerHTML = the;
node.content.appendChild(style);
});
insertHTML();
});
} else {
insertHTML();
}
//插入HTML
function insertHTML() {
container.appendChild(document.importNode(node.content, !0));
for (let script of node.content.querySelectorAll(`script`)) {
if (script.getAttribute("src")) {
that
.fetch({
url: script.src,
headers: { "Content-type": "text/html" },
returnData(res) {
return res.text();
},
})
.then(res => {
eval(res);
});
} else {
eval(script.innerHTML);
}
}
success();
}
})
.catch(err => {
console.error(`[${Alphabet[8]} - Router] 不存在以下路由:${err.target.responseURL}`);
fail(`${route}`);
});
} else {
container.appendChild(document.importNode(temp.content, !0));
success();
}
} else {
console.error(`[${Alphabet[8]} - Router] 不存在以下路由:${route}`);
fail(`${route}`);
}
});
};
//获取url参数并转换成对象
this.getParams = () => {
const url = location.href.split(`?`);
if (location.href.indexOf(`?`) > -1) {
let obj = {};
if (url[1].split(`&`)) {
const params = url[1].split(`&`);
params.map(v => {
obj[v.split(`=`)[0]] = v.split(`=`)[1];
});
} else {
obj[url[1].split(`=`)[0]] = obj[url[1].split(`=`)[1]];
}
return obj;
} else {
return null;
}
};
// HASH改变
this.hashChange = (callback, routes) => {
const getRoutePath = () => {
if (location.hash.indexOf(`#`) > -1) {
return location.hash.match(/#(\S*)\?/) === null ? location.hash.match(/#(\S*)/).input.replace(`#`, ``) : location.hash.match(/#(\S*)\?/).input.replace(`#`, ``);
} else {
return !1;
}
};
const routePath = getRoutePath() || routes[0].path;
callback(routePath);
};
// 数组相关方法
this.Array = {
// 原始数组
originals: this.get,
// 随机打乱数组
Random() {
let originals = this.originals;
for (let i = 0; i < originals.length; i++) originals[i] = originals[i];
originals.sort(() => {
return 0.5 - Math.random();
});
return originals;
},
// 判断是否存在重复
hasRepeat() {
const originals = this.originals;
let hash = {};
for (let i in originals) {
if (hash[originals[i]]) {
return !0;
}
hash[originals[i]] = !0;
}
return !1;
},
// 数组求和
Sum() {
const arr = this.originals;
let s = 0;
for (let i = arr.length - 1; i >= 0; i--) s += arr[i];
return s;
},
};
}
}
//拓展方法
const PandoraJs = SuperClass => {
return class extends SuperClass {
constructor(obj) {
super(obj);
}
//Mustache渲染
Mush(options) {
let config = {
//渲染数据(类型:对象)
data: null,
//生命周期-首次渲染完成(类型:方法;返回初始渲染数据)
Init: null,
// 生命周期-每次更新渲染完成(类型:方法;返回最新渲染数据)
Update: null,
};
config = this.extend(config, options);
let Html = this.html(),
bHtml = Html,
matchValue;
const that = this,
{ data, Init, Update } = config,
pattern = new RegExp("{{.*?}}", "g"),
patterns = new RegExp("{{.*?}}");
// 重构渲染数据
const getObj = res => {
let newObj = {};
for (let keyName of Object.keys(res)) newObj[keyName] = res[keyName];
return newObj;
};
// 获取所有MUSH变量
const getMush = () => {
let r = [];
Html.match(pattern).forEach((e, index) => {
r[index] = e.split(`{{`)[1].split(`}}`)[0];
});
return r;
};
matchValue = getMush();
//渲染html
const renderHtml = () => {
return new Promise(next => {
Html = bHtml;
for (let value of matchValue) {
for (let keyName in data) {
if (value === keyName) {
Html = Html.replace(patterns, data[value] + "" || "");
}
}
}
that.html(Html);
next();
});
};
// 返回所有唯一变量
const unique = array => {
let r = [];
for (let i = 0, l = array.length; i < l; i++) {
for (let j = i + 1; j < l; j++) {
if (array[i] == array[j]) {
j == ++i;
}
}
r.push(array[i]);
}
return r;
};
//遍历变量是否被动态修改
unique(matchValue).forEach(e => {
Object.defineProperty(that.globalData, e, {
set(value) {
data[e] = value;
renderHtml()
.then(() => {
Update && Update(getObj(that.globalData));
})
.catch(err => {
console.error(`[${Alphabet[8]} - Mush] 变量更新失败`, err);
});
},
get() {
return data[e];
},
enumerable: !0,
});
});
renderHtml()
.then(() => {
Init && Init(getObj(that.globalData));
})
.catch(err => {
console.error(`[${Alphabet[8]} - Mush] 初始渲染失败`, err);
});
return this;
}
//静态路由
Router(options) {
let config = {
// 路由路径集合(类型:数组)
routes: null,
};
config = this.extend(config, options);
const that = this,
{ routes } = config;
// 初始化路由
const initRoute = () => {
that
.template(routes[0].path.split("?")[0], that.get)
.then(() => {
that.currentRoute = routes[0].path.split("?")[0];
try {
routes[0].callback && routes[0].callback(that.getParams() ? that.getParams() : {});
} catch (err) {
routes[0].error && routes[0].error(err);
console.error(`[${Alphabet[8]} - Router]`, err);
}
})
.catch(err => {
routes[0].error && routes[0].error(err);
console.error(`[${Alphabet[8]} - Router]`, err);
});
};
// 遍历路由路径
const eachRoutes = path => {
if (path) {
if (JSON.stringify(routes).indexOf(path.split("?")[0]) < 0) {
console.error(`[${Alphabet[8]} - Router] ${path.split("?")[0]} 不存在于routes`);
} else {
try {
routes.forEach(e => {
if (path.split("?")[0] == e.path) {
that
.template(path.split("?")[0], that.get)
.then(() => {
that.go(path);
that.currentRoute = path;
try {
e.callback && e.callback(that.getParams() ? that.getParams() : {});
} catch (err) {
e.error && e.error(err);
console.error(`[${Alphabet[8]} - Router]`, err);
}
})
.catch(err => {
e.error && e.error(err);
console.error(`[${Alphabet[8]} - Router]`, err);
});
}
});
} catch (err) {
console.error(`[${Alphabet[8]} - Router]`, err);
}
}
} else {
console.error(`[${Alphabet[8]} - Router] 不存在任何路由`);
}
};
// 返回上一路由
that.back = () => {
setTimeout(() => {
w.history.back();
});
};
// 路由导航
that.go = path => {
setTimeout(() => {
w.location.href = `#${path}`;
});
};
// 路由重定向
that.direct = path => {
setTimeout(() => {
w.location.replace(`#${path}`);
});
};
// 路由重载
that.reload = () => {
eachRoutes(w.location.hash.split("#")[1]);
};
// 初始化
if (routes) {
if (w.location.hash.indexOf("#") < 0) {
initRoute();
} else {
if (w.location.hash.split("#")[1] == routes[0].path) {
initRoute();
} else {
eachRoutes(w.location.hash.split("#")[1]);
}
}
w.onhashchange = () => {
that.hashChange(eachRoutes, routes);
};
} else {
return console.error(`[${Alphabet[8]} - Router] 不存在任何路由!`);
}
return this;
}
//轮播切换
Switcher(options) {
let config = {
//过渡速度/秒(类型:数字)
Speed: 1,
//动画曲线(类型字符串参考css3动画曲线)
Curve: "ease",
//切换效果(类型字符串可选参数slider、fade)
Effect: "slider",
//方向(类型字符串可选参数horizontal、vertical)
Direction: "horizontal",
//惯性回弹(类型:布尔)
Inertia: !0,
//滑动比例(类型:数字)
Distance: 3,
//自动切换间隔/秒(类型数字为0时不自动切换)
AutoSpeed: 0,
//分页器(类型:布尔)
Pagination: !1,
//悬浮停止(类型:布尔)
Hover: !1,
//滚轮滚动(类型:布尔)
Scroll: !1,
//初始坐标(类型:数字)
InitPage: 0,
//循环(类型:布尔)
Loop: !1,
//切换状态变化(类型:方法)
onChange: null,
//是否窗口大小改变时自动调整(类型:布尔)
AutoResize: !1,
};
config = this.extend(config, options);
const { Speed, Curve, Effect, Direction, Inertia, Distance, AutoSpeed, Pagination, Hover, Scroll, InitPage, Loop, onChange, AutoResize } = config,
childEle = this.get,
parentEle = childEle[0].parentElement,
total = childEle.length,
transitionend = () => {
if (isScrolling) {
isScrolling = !1;
parentEle.removeEventListener("transitionend", transitionend);
}
};
let currentPage = InitPage,
childW = childEle[0].offsetWidth,
childH = childEle[0].offsetHeight,
AutoTimeout,
isScrolling = !1,
isFristTime = !0;
//切换
const Swiper = (moveTo = null) => {
if (typeof moveTo == "number") currentPage = moveTo;
Pager(currentPage);
if (!isFristTime) {
onChange && onChange(currentPage);
}
switch (Effect) {
case "fade":
for (let cur of childEle) {
if (cur.className.indexOf("active") > -1) {
cur.style.opacity = 1;
cur.style.zIndex = 2;
} else {
cur.style.opacity = 0;
cur.style.zIndex = 1;
}
}
break;
default:
switch (Direction) {
case "vertical":
parentEle.style.transform = `translate3d(0,${-1 * (childH * currentPage)}px,0)`;
break;
case "horizontal":
default:
parentEle.style.transform = `translate3d(${-1 * (childW * currentPage)}px,0,0)`;
break;
}
break;
}
if (Loop) {
parentEle.addEventListener("transitionend", transitionend);
} else {
if (currentPage === 0 || currentPage === total - 1) {
transitionend();
} else {
parentEle.addEventListener("transitionend", transitionend);
}
}
};
//分页器
const Pager = current => {
for (let e of childEle) e.className = e.className.replace(Alphabet[0], "").trim();
if (childEle[currentPage].className) {
childEle[currentPage].className += ` ${Alphabet[0]}`;
} else {
childEle[currentPage].className += Alphabet[0];
}
if (Pagination) {
parentEle.parentElement.querySelector(`.Pd-pagination`) && parentEle.parentElement.removeChild(parentEle.parentElement.querySelector(`.Pd-pagination`));
const pager = document.createElement("div");
pager.className = "Pd-pagination";
for (let a = 0; a < total; a++) {
const pageChild = document.createElement("a"),
textNode = childEle[a].getAttribute(`data-title`) ? document.createTextNode(childEle[a].getAttribute(`data-title`)) : document.createTextNode(a);
pageChild.setAttribute("href", "javascript:void 0");
if (a === current) pageChild.className = Alphabet[0];
pageChild.appendChild(textNode);
pager.appendChild(pageChild);
}
parentEle.parentElement.insertBefore(pager, parentEle.nextElementSibling);
for (let a = 0; a < parentEle.parentElement.querySelectorAll(`.Pd-pagination a`).length; a++) {
const e = parentEle.parentElement.querySelectorAll(`.Pd-pagination a`)[a],
idx = a;
e.onclick = () => {
currentPage = idx;
Swiper();
};
}
}
};
//上一个
const Prev = () => {
isFristTime = !1;
if (currentPage < total && currentPage > 0) {
currentPage--;
} else if (currentPage === 0 && Loop) {
currentPage = total - 1;
} else {
isScrolling = !1;
}
Swiper();
};
//下一个
const Next = () => {
isFristTime = !1;
if (currentPage < total - 1) {
currentPage++;
} else if (currentPage === total - 1 && Loop) {
currentPage = 0;
} else {
currentPage = total - 1;
}
Swiper();
};
let startX, startY, endX, endY, curX, curY;
//方法:滑动开始
const touchStart = event => {
clearTimeout(AutoTimeout);
cancelAnimationFrame(AutoPlayFrame);
const { pageX, pageY } = event.changedTouches[0];
const { left, top } = parentEle.parentElement.getBoundingClientRect();
switch (config.Direction) {
case "vertical":
startY = pageY - top;
break;
case "horizontal":
default:
startX = pageX - left;
break;
}
parentEle.style.transition = null;
};
//方法:滑动中
const touchMove = event => {
const { pageX, pageY } = event.changedTouches[0],
{ left, top } = parentEle.parentElement.getBoundingClientRect();
curX = pageX - left;
curY = pageY - top;
switch (Effect) {
case "fade":
for (let cur of childEle) cur.style.transition = `opacity ${Speed}s linear`;
break;
default:
switch (Direction) {
case "vertical":
if (startY > curY) {
if (currentPage != total - 1) parentEle.style.transform = `translate3d(0,${-1 * (startY - curY + childH * currentPage)}px,0)`;
} else {
if (currentPage != 0) parentEle.style.transform = `translate3d(0,${-1 * (childH * currentPage) + Math.abs(curY - startY)}px,0)`;
}
break;
case "horizontal":
default:
if (startX > curX) {
parentEle.style.transform = `translate3d(${-1 * (startX - curX + childW * currentPage)}px,0,0)`;
} else {
parentEle.style.transform = `translate3d(${-1 * (childW * currentPage) + Math.abs(curX - startX)}px,0,0)`;
}
break;
}
break;
}
};
//方法:滑动结束
const touchEnd = event => {
clearTimeout(AutoTimeout);
AutoPlay();
parentEle.style.transition = `transform ${Speed}s ${Curve}`;
const { pageX, pageY } = event.changedTouches[0],
{ left, top } = parentEle.parentElement.getBoundingClientRect();
switch (Direction) {
case "vertical":
endY = pageY - top;
switch (Effect) {
case "fade":
if (startY - endY > childH / config.Distance && currentPage === total - 1) {
currentPage = 0;
} else if (startY - endY > childH / config.Distance && currentPage < total - 1) {
Next();
} else if (endY - startY > childH / config.Distance) {
Prev();
}
for (let cur of childEle) {
cur.style.transition = `opacity ${config.Speed}s ${config.Curve}`;
cur.style.opacity = 0;
cur.style.zIndex = 1;
}
childEle[currentPage].style.opacity = 1;
childEle[currentPage].style.zIndex = 2;
Swiper();
break;
default:
if (startY - endY > childH / config.Distance && currentPage < total - 1) Next();
if (endY - startY > childH / config.Distance) Prev();
parentEle.style.transform = `translate3d(0,${-1 * (childH * currentPage)}px,0)`;
break;
}
break;
case "horizontal":
default:
endX = pageX - left;
switch (Effect) {
case "fade":
if (startX - endX > childW / Distance && currentPage === total - 1) {
currentPage = 0;
} else if (startX - endX > childW / Distance && currentPage < total - 1) {
Next();
} else if (endX - startX > childW / Distance) {
Prev();
}
for (let cur of childEle) {
cur.style.transition = `opacity ${Speed}s ${Curve}`;
cur.style.opacity = 0;
cur.style.zIndex = 1;
}
childEle[currentPage].style.opacity = 1;
childEle[currentPage].style.zIndex = 2;
Swiper();
break;
default:
if (startX - endX > childW / Distance && currentPage < total - 1) Next();
if (endX - startX > childW / Distance) Prev();
parentEle.style.transform = `translate3d(${-1 * (childW * currentPage)}px,0,0)`;
break;
}
break;
}
};
//方法:滚动中
const scrollMove = event => {
event.preventDefault();
if (event.deltaY > 20 && !isScrolling) {
isScrolling = !0;
Next();
}
if (event.deltaY < -20 && !isScrolling) {
isScrolling = !0;
Prev();
}
};
//自动播放
let AutoPlayFrame;
const AutoPlay = () => {
if (AutoSpeed) {
AutoTimeout = setTimeout(() => {
Next();
clearTimeout(AutoTimeout);
AutoPlayFrame = requestAnimationFrame(AutoPlay);
}, AutoSpeed * 1000);
}
};
//初始化
function Init() {
const { width, height } = parentEle.parentElement.getBoundingClientRect();
childW = width;
childH = height;
currentPage = InitPage;
return new Promise(next => {
switch (Effect) {
case "fade":
for (let cur of childEle) {
cur.style.transition = `opacity ${Speed}s ${Curve}`;
cur.style.position = "absolute";
}
parentEle.style.width = `${width}px`;
parentEle.style.height = `${height}px`;
break;
default:
switch (Direction) {
case "vertical":
parentEle.style.width = `${width}px`;
parentEle.style.height = `${height * total}px`;
parentEle.style.flexDirection = "column";
parentEle.style.cssText += "touch-action: pan-x";
break;
case "horizontal":
default:
parentEle.style.width = `${width * total}px`;
parentEle.style.height = `${height}px`;
parentEle.style.flexDirection = "row";
parentEle.style.cssText += "touch-action: pan-y";
break;
}
parentEle.style.display = "flex";
parentEle.style.transition = `transform ${Speed}s ${Curve}`;
break;
}
//移除事件
Swiper(InitPage);
AutoPlay();
Inertia && parentEle.removeEventListener("touchmove", touchMove);
Scroll && parentEle.removeEventListener("mousewheel", scrollMove);
parentEle.removeEventListener("touchstart", touchStart);
parentEle.removeEventListener("touchend", touchEnd);
//添加事件
Inertia && parentEle.addEventListener("touchmove", touchMove);
Scroll && parentEle.addEventListener("mousewheel", scrollMove);
parentEle.addEventListener("touchstart", touchStart);
parentEle.addEventListener("touchend", touchEnd);
if (Hover) {
parentEle.addEventListener("mouseover", () => {
clearTimeout(AutoTimeout);
cancelAnimationFrame(AutoPlayFrame);
});
parentEle.addEventListener("mouseout", AutoPlay);
}
next();
});
}
this.prev = Prev;
this.next = Next;
this.direct = index => {
isFristTime = !1;
Swiper(index);
};
this.disable = () => {
Inertia && parentEle.removeEventListener("touchmove", touchMove);
Scroll && parentEle.removeEventListener("mousewheel", scrollMove);
parentEle.removeEventListener("touchstart", touchStart);
parentEle.removeEventListener("touchend", touchEnd);
clearTimeout(AutoTimeout);
cancelAnimationFrame(AutoPlayFrame);
};
this.enable = () => {
//添加事件
Inertia && parentEle.addEventListener("touchmove", touchMove);
Scroll && parentEle.addEventListener("mousewheel", scrollMove);
parentEle.addEventListener("touchstart", touchStart);
parentEle.addEventListener("touchend", touchEnd);
if (Hover) {
parentEle.addEventListener("mouseover", () => {
clearTimeout(AutoTimeout);
cancelAnimationFrame(AutoPlayFrame);
});
parentEle.addEventListener("mouseout", AutoPlay);
}
if (AutoSpeed) {
AutoTimeout = setTimeout(() => {
Next();
clearTimeout(AutoTimeout);
AutoPlayFrame = requestAnimationFrame(AutoPlay);
}, AutoSpeed * 1000);
}
};
Init();
if (AutoResize) {
let isResizing = !1;
let resizeTimer;
w.onresize = () => {
clearTimeout(resizeTimer);
resizeTimer = null;
if (!isResizing) {
isResizing = !0;
resizeTimer = setTimeout(() => {
Init();
isResizing = !1;
}, 300);
}
};
}
return this;
}
//页面&字体自适应
AutoSize(options) {
let config = {
//固定尺寸(类型:字符串)
PageSize: "device-width",
//初始缩放(类型:数字)
InitScale: 1,
//最小缩放(类型:数字)
MinScale: 1,
//最大缩放(类型:数字)
MaxScale: 1,
//DPI缩放(类型:数字;默认window.devicePixelRatio)
Ratio: null,
//窗口变化重新计算(类型:布尔)
Resize: !0,
//是否缩放字体大小(类型:布尔)
ScaleFont: !0,
};
config = this.extend(config, options);
const meta = document.createElement(`meta`),
{ PageSize, InitScale, MinScale, MaxScale, Resize, ScaleFont } = config;
let Ratio = config.Ratio || w.devicePixelRatio;
meta.setAttribute("name", "viewport");
if (typeof PageSize !== "number") {
meta.setAttribute("content", `width=${PageSize},initial-scale=${InitScale},minimum-scale=${MinScale},maximum-scale=${MaxScale},user-scalable=no,viewport-fit=cover`);
} else {
meta.setAttribute("content", `width=${PageSize},user-scalable=no,viewport-fit=cover`);
}
if (document.querySelector("meta[name='viewport']")) {
document.querySelector("meta[name='viewport']").remove();
}
document.head.appendChild(meta);
if (ScaleFont) {
//根据屏幕尺寸设置根元素字体大小
const SetSize = () => {
const { clientWidth: width, clientHeight: height } = document.documentElement;
document.documentElement.style.fontSize = `${(Math.min(width, height) / 10) * Ratio}px`;
};
SetSize();
if (Resize) w.onresize = SetSize;
}
return this;
}
//自定义弹框
Dialog(options) {
let config = {
//是否显示遮罩
mask: !0,
//遮罩颜色(类型:字符串)
maskColor: "rgba(0,0,0,.85)",
//点击遮罩退出(类型:布尔)
maskOut: !0,
//过渡速度/毫秒(类型:数字)
Speed: 180,
//过渡曲线(类型字符串参考CSS3可用曲线)
Curve: "ease-out",
//进出方式(类型字符串none:无、zoom:缩放、top:从屏幕上方出现、bottom:从屏幕下方出现)
Direction: "zoom",
//进入事件(类型:方法)
In: null,
//退出事件(类型:方法)
Out: null,
//确认事件
Confirm: {
//确定按钮(类型:字符串)
btn: null,
//回调(类型:方法;返回类型:对象)
callback: null,
},
//取消事件
Cancel: {
//取消按钮(类型:字符串)
btn: null,
//回调(类型:方法;返回类型:对象)
callback: null,
},
};
config = this.extend(config, options);
const masker = document.createElement(`div`),
parent = this.get.parentElement,
{ mask, maskColor, maskOut, Speed, Curve, Direction, In, Out, Confirm, Cancel } = config;
masker.className = "Pd-Mask";
const confirmBtn = Confirm.btn ? new PandoraEX(Confirm.btn) : null,
cancelBtn = Cancel.btn ? new PandoraEX(Cancel.btn) : null;
if (Direction !== "none") this.css({ transition: `all ${Speed}ms ${Curve}` });
//关闭弹框
const closeDialog = () => {
return new Promise(next => {
if (this.css("display") == "block" || this.css("display") == "flex") {
Effect(`out`);
if (Direction === "none") {
try {
mask && parent.removeChild(masker);
} catch (err) {
console.error(`[${Alphabet[8]} - Dialog]`, err);
}
this.css({ display: "none" });
next();
} else {
this.bind("transitionend", () => {
try {
mask && parent.removeChild(masker);
} catch (err) {
console.error(`[${Alphabet[8]} - Dialog]`, err);
}
this.css({ display: "none" });
this.unbind("transitionend");
next();
});
}
} else {
next();
}
Confirm.btn && confirmBtn.unbind(`click`);
Cancel.btn && cancelBtn.unbind(`click`);
w.onresize = null;
});
};
//进入和退出效果
const Effect = where => {
if (mask && where === "in") {
parent.insertBefore(masker, this.get.nextElementSibling);
document.querySelector(`.Pd-Mask`).style.cssText = `width:100vw;height:100vh;background:${maskColor};position:fixed;inset:0;top:0;left:0;right:0;bottom:0;z-index:998;`;
}
switch (where) {
case "in":
this.css({ display: "block" });
switch (Direction) {
case "none":
this.css({ display: "block" });
break;
case "top":
this.css({ transform: "translate3d(0,-100%,0)" });
break;
case "bottom":
this.css({ transform: "translate3d(0,100%,0)" });
break;
case "zoom":
this.css({ transform: "translate3d(0,0,0) scale(0)" });
break;
}
In && In();
break;
case "out":
switch (Direction) {
case "none":
this.css({ display: "none" });
break;
case "top":
this.css({ transform: "translate3d(0,-100%,0)" });
break;
case "bottom":
this.css({ transform: "translate3d(0,100%,0)" });
break;
case "zoom":
this.css({ transform: "translate3d(0,0,0) scale(0)" });
break;
}
Out && Out();
break;
}
};
//打开弹框
const openDialog = param => {
this.unbind("transitionend");
for (let a of w.pdDialogs) {
if (a != this) a.close();
}
Effect(`in`);
return new Promise(next => {
const calcDialog = () => {
const top = parseInt(this.css(`height`)) / 2,
left = parseInt(this.css(`width`)) / 2;
switch (Direction) {
case "none":
this.css({ position: "fixed", top: `calc(50% - ${top}px)`, left: `calc(50% - ${left}px)`, "z-index": 999 });
break;
case "top":
this.css({ position: "fixed", top: 0, left: `calc(50% - ${left}px)`, "z-index": 999, transform: "translate3d(0,0,0) scale(1)" });
break;
case "bottom":
this.css({ position: "fixed", bottom: 0, left: `calc(50% - ${left}px)`, "z-index": 999, transform: "translate3d(0,0,0) scale(1)" });
break;
case "zoom":
default:
this.css({ position: "fixed", top: `calc(50% - ${top}px)`, left: `calc(50% - ${left}px)`, "z-index": 999, transform: "translate3d(0,0,0) scale(1)" });
break;
}
};
calcDialog();
w.onresize = () => {
this.bind("transitionend", calcDialog);
};
//遮罩被点击
if (mask && maskOut) masker.onclick = closeDialog;
const { close } = this;
//确认按钮被点击
Confirm.btn &&
confirmBtn.bind("click", () => {
Confirm.callback({ param: param || null, close });
});
//取消按钮被点击
Cancel.btn &&
cancelBtn.bind("click", () => {
Cancel.callback({ param: param || null, close });
});
next();
});
};
this.close = closeDialog;
this.open = openDialog;
w.pdDialogs.push(this);
return this;
}
//图片预加载
ImgLoader(options) {
let config = {
//渐进式(类型:布尔)
lazy: !0,
//加载中(类型:方法;返回类型:数字)
loading: null,
//加载完成(类型:方法)
callback: null,
//加载错误(类型:方法)
error(err) {
console.error(`[${Alphabet[8]} - ImgLoader] 资源加载错误!\n${err}`);
alert("资源加载错误!");
},
};
config = this.extend(config, options);
const { lazy, loading, callback, error } = config;
let ImgArr = [],
total = 0,
cur = 0,
step = 0,
floatNum = 0;
// 类型检查
const typeCheck = str => {
if (str.indexOf(`url`) > -1 && str != "none" && str.indexOf(`data:`) < 0 && str.indexOf(`blob:`) < 0) {
return !0;
} else {
return !1;
}
};
// 仅容器内获取
const onlyContainer = (ele, callout) => {
const pattern = new RegExp('".*?"', "g"),
pattern2 = new RegExp(/'.*?'/, "g"),
pattern3 = new RegExp(/\(.*?\)/, "g");
for (let e of ele.querySelectorAll("*")) {
if (e.nodeName.toLowerCase() == "img") {
if (e.src) {
ImgArr.push(e.src);
}
}
const getBg = w.getComputedStyle(e).getPropertyValue(`background-image`);
if (typeCheck(getBg)) {
const url1 = getBg.match(pattern),
url2 = getBg.match(pattern2),
url3 = getBg.match(pattern3);
url1 && ImgArr.push(url1[0].toString().replace(/"/g, ""));
url2 && ImgArr.push(url2[0].toString().replace(/'/g, ""));
if (url3) {
let src = url3[0].toString().replace(/\(/, "").replace(/\)/, "");
if (src.match(pattern)) src = src.match(pattern)[0].toString().replace(/"/g, "");
if (src.match(pattern2)) src = src.match(pattern2)[0].toString().replace(/'/g, "");
ImgArr.push(src);
}
}
}
callout();
};
// 全局获取
const allResources = callout => {
const pattern1 = new RegExp(`background-image: url(.*?)*`, "g"),
pattern2 = new RegExp(`background: url(.*?)*`, "g");
// 截取背景图
const getThat = (type, str) => {
let src;
switch (type) {
case 1:
src = str.split(`background-image: url(`)[1].split(`)`)[0];
break;
case 2:
src = str.split(`background: url(`)[1].split(`)`)[0];
break;
}
if (src.indexOf(`"`) > -1) return src.replace(/"/g, ``);
if (src.indexOf(`'`) > -1) return src.replace(/'/g, ``);
return src;
};
document.querySelectorAll("link").forEach(css => {
if (css.getAttribute("rel") == "stylesheet") {
this.ajax({
url: css.getAttribute("href"),
responseType: "text",
}).then(str => {
if (str.match(pattern1)) {
for (let a of str.match(pattern1)) {
typeCheck(a) && ImgArr.push(getThat(1, a));
}
}
if (str.match(pattern2)) {
for (let a of str.match(pattern2)) {
typeCheck(a) && ImgArr.push(getThat(2, a));
}
}
});
}
});
document.querySelectorAll("style").forEach(css => {
const str = css.innerText;
if (str.match(pattern1)) {
for (let a of str.match(pattern1)) {
typeCheck(a) && ImgArr.push(getThat(1, a));
}
}
if (str.match(pattern2)) {
for (let a of str.match(pattern2)) {
typeCheck(a) && ImgArr.push(getThat(2, a));
}
}
});
const bodyStr = document.body.innerHTML;
if (bodyStr.match(pattern1)) {
for (let a of bodyStr.match(pattern1)) {
typeCheck(a) && ImgArr.push(getThat(1, a));
}
}
if (bodyStr.match(pattern2)) {
for (let a of bodyStr.match(pattern2)) {
typeCheck(a) && ImgArr.push(getThat(2, a));
}
}
for (let e of document.querySelectorAll("img")) {
e.src && ImgArr.push(e.src);
}
callout();
};
// 加载全部并回调
const finalStep = () => {
total = ImgArr.length;
const loader = src => {
return new Promise((success, fail) => {
const img = new Image();
img.src = src;
img.onerror = fail;
if (img.complete) {
cur++;
success();
} else {
img.onload = () => {
cur++;
success();
};
}
});
};
//加载中
let loadStepFrame;
Promise.all(ImgArr.map(e => loader(e))).catch(err => {
cancelAnimationFrame(loadStepFrame);
error(err);
console.error(`[${Alphabet[8]} - ImgLoader]`, err);
});
const loadStep = () => {
step = Math.floor((cur / total) * 100);
if (floatNum < 100) {
if (floatNum < step) lazy ? floatNum++ : (floatNum = step);
loading && loading(floatNum);
if (floatNum === 100) {
cancelAnimationFrame(loadStepFrame);
if (lazy) {
callback && callback();
} else {
setTimeout(callback);
}
} else {
loadStepFrame = requestAnimationFrame(loadStep);
}
}
};
loadStep();
};
this.get ? onlyContainer(this.get, finalStep) : allResources(finalStep);
return this;
}
//图片上传
ImgUpload(options) {
let config = {
//接口地址(类型:字符串)
apiUrl: null,
//格式限制(类型:字符串)
Format: "*",
//选择类型(可选参数default、camera)
type: "default",
//限制数量(类型:数字)
Max: 1,
//压缩比例(类型:数字)
Quality: 100,
//尺寸裁切
Clip: {
//宽度(类型:数字)
width: null,
//高度(类型:数字)
height: null,
},
//是否总是覆盖
alwaysCover: !1,
//上传事件
Events: {
//超过限制(类型:方法)
overMax: null,
//开始上传(类型:方法)
ready: null,
//上传中(类型:方法;返回类型:数字)
progress: null,
//上传成功(类型:方法;返回类型:对象)
success: null,
//失败(类型:方法)
fail() {
console.error(`[${Alphabet[8]} - ImgUpload] 上传失败!`);
},
},
//唯一id(类型字符串如果为null则启用临时上传请谨慎使用)
Uid: null,
};
config = this.extend(config, options);
const innerHtml = this.html(),
{ apiUrl, Format, type, Max, Quality, Clip, alwaysCover, Events, Uid } = config;
this.empty();
this.get.insertAdjacentHTML("afterbegin", `<label for="Pd_imgUpload_${this.pid}" style="width:100%;height:100%;display:block"></label>`);
const uploadBtn = document.createElement(`input`);
let userId,
total = 0,
currentIndex = 0,
stepsTotal = 0,
stepsOnly = 0,
finalTotal = 0;
if (Uid) {
userId = Uid;
} else {
userId = `${document.domain}_${this.pid}`;
}
uploadBtn.type = "file";
uploadBtn.accept = `image/${Format}`;
uploadBtn.id = `Pd_imgUpload_${this.pid}`;
uploadBtn.setAttribute("capture", type);
uploadBtn.hidden = !0;
if (Max > 1) uploadBtn.multiple = !0;
const label = this.get.querySelector(`label`);
label.innerHTML = innerHtml;
label.append(uploadBtn);
//上传图片
const uploadPreview = obj => {
const formData = new FormData();
let waitUploadFile = obj;
if (alwaysCover) waitUploadFile = new File([obj], `cover.${obj.name.split(".")[1]}`, { type: obj.type });
formData.append("images", waitUploadFile);
formData.append("uid", userId);
formData.append("width", Clip.width);
formData.append("height", Clip.height);
formData.append("quality", Quality);
Events.ready && Events.ready();
this.ajax({
url: `${apiUrl}`,
type: "post",
dataType: "form",
async: !0,
headers: null,
data: formData,
progress(progress) {
if (total > 1) {
if (progress == 100) currentIndex++;
stepsTotal = Math.floor((currentIndex / total) * 100);
Events.progress(stepsTotal);
} else {
stepsOnly = progress;
Events.progress(stepsOnly);
}
},
})
.then(res => {
if (res) {
if (total > 1) {
finalTotal = stepsTotal;
} else {
finalTotal = stepsOnly;
}
uploadBtn.setAttribute("data-progress", finalTotal);
if (finalTotal === 100) {
const data = { src: res.images };
Events.success && Events.success(data);
}
} else {
alert("发生错误!");
console.error(`[${Alphabet[8]} - ImgUpload] 服务端错误!`);
}
})
.catch(Events.fail);
};
//获取选择文件
const selectedFile = Files => {
const files = Array.prototype.slice.call(Files);
(total = 0), (currentIndex = 0), (stepsTotal = 0), (stepsOnly = 0), (finalTotal = 0);
if (Max === 0 || files.length <= Max) {
total = files.length;
if (total > 0) {
files.forEach((file, idx) => {
uploadPreview(Files[idx]);
});
}
} else {
Events.overMax && Events.overMax();
console.info(`[${Alphabet[7]} - ImgUpload] 超过最大数量:${Max}`);
}
};
//选择文件按钮事件
uploadBtn.addEventListener("change", event => {
event.preventDefault();
selectedFile(event.target.files);
});
//拖动文件事件
this.bind("dragover", event => {
event.preventDefault();
});
this.bind("drop", event => {
event.preventDefault();
selectedFile(event.dataTransfer.files);
});
return this;
}
//图片移动缩放
ImgTransit(options) {
let config = {
//显示控制图标(类型:布尔)
icon: !0,
//控制图标大小(类型:数字)
iconSize: 30,
//显示边框(类型:布尔)
border: !0,
//开启多点触控(类型:布尔)
Gesture: !1,
//内边距(类型:数字)
padding: 10,
//缩放
scale: {
//是否启用(类型:布尔)
enable: !0,
//最小(类型:数字)
min: 80,
//最大(类型:数字)
max: 150,
//速率(类型:数字)
rate: 1,
},
//旋转
rotate: {
//是否启用(类型:布尔)
enable: !0,
//速率(类型:数字)
rate: 1,
},
//是否启用删除(类型:布尔)
delete: !0,
//边界限制(类型:布尔)
bounds: !0,
//边界可超出范围(类型:数字)
outBounds: 0,
//操作回调方法(类型:方法;返回类型:对象)
callback: null,
};
config = this.extend(config, options);
const that = this.get,
btnAnimation = "transition:opacity .2s ease-in",
imgs = that.querySelectorAll("img");
this.hide();
// 创建容器
if (!document.querySelector(".PD-TransitBox")) {
that.insertAdjacentHTML("afterend", "<div class='PD-TransitBox'></div>");
document.querySelector(".PD-TransitBox").style.cssText = `position:relative;touch-action:none;`;
}
const TransitBox = document.querySelector(".PD-TransitBox");
for (let the of imgs) {
TransitBox.appendChild(the);
the.parentNode.removeChild(the);
}
//图标配置
const iconStyle = option => {
let positionConfig = { top: null, left: null, right: null, bottom: null, name: null };
positionConfig = this.extend(positionConfig, option);
return `<a class="Pd-ImgTransit-btn Pd-${positionConfig.name}" style="width:${config.iconSize}px;height:${config.iconSize}px;background:#fff url('${
icoConfig[positionConfig.name]
}');background-position:center;background-repeat:no-repeat;background-size:65%;position:absolute;border-radius:50%;top:${positionConfig.top}px;left:${positionConfig.left}px;right:${
positionConfig.right
}px;bottom:${positionConfig.bottom}px;z-index:2;${btnAnimation}" href="javascript:void 0"></a>`;
};
const icon = {
resize: iconStyle({ left: `-${config.iconSize / 2}`, bottom: `-${config.iconSize / 2}`, name: "resize" }),
rotate: iconStyle({ right: `-${config.iconSize / 2}`, top: `-${config.iconSize / 2}`, name: "rotate" }),
delete: iconStyle({ left: `-${config.iconSize / 2}`, top: `-${config.iconSize / 2}`, name: "delete" }),
};
//设置参数
const setConfig = (ele, eleConfig) => {
for (let a of ele.querySelectorAll(`.Pd-ImgTransit-btn`)) a.style.transform = `scale(${1 / (eleConfig.scale / 100)}) rotate(${-1 * eleConfig.rotate}deg)`;
return (ele.style.transform = `translate3d(${eleConfig.translate}) scale(${eleConfig.scale / 100}) rotate(${eleConfig.rotate}deg)`);
};
//获取中心
const getCenterPoint = ele => {
return {
x: ele.getBoundingClientRect().left + ele.offsetWidth / 2,
y: ele.getBoundingClientRect().top + ele.offsetHeight / 2,
};
};
// 获取两点距离
const getDistance = (p1, p2) => {
const x = p2.pageX - p1.pageX,
y = p2.pageY - p1.pageY;
return Math.sqrt(x * x + y * y);
};
// 获取两点角度
const getAngle = (p1, p2) => {
const x = p1.pageX - p2.pageX,
y = p1.pageY - p2.pageY;
return (Math.atan2(y, x) * 180) / Math.PI;
};
// 多点触控
const setGesture = el => {
let obj = {}; //定义一个对象
let isTouch = !1;
let start = [];
el.addEventListener(
"touchstart",
e => {
if (e.touches.length >= 2) {
//判断是否有两个点在屏幕上
isTouch = !0;
start = e.touches; //得到第一组两个点
obj.gesturestart && obj.gesturestart.call(el); //执行gesturestart方法
}
},
!1
);
document.addEventListener(
"touchmove",
e => {
e.preventDefault();
if (e.touches.length >= 2 && isTouch) {
const now = e.touches, //得到第二组两个点
scale = getDistance(now[0], now[1]) / getDistance(start[0], start[1]), //得到缩放比例getDistance是勾股定理的一个方法
rotation = getAngle(now[0], now[1]) - getAngle(start[0], start[1]); //得到旋转角度getAngle是得到夹角的一个方法
e.scale = scale.toFixed(2);
e.rotation = rotation.toFixed(2);
obj.gesturemove && obj.gesturemove.call(el, e); //执行gesturemove方法
}
},
!1
);
document.addEventListener(
"touchend",
() => {
if (isTouch) {
isTouch = !1;
obj.gestureend && obj.gestureend.call(el); //执行gestureend方法
}
},
!1
);
return obj;
};
// 添加事件
const addEvent = ele => {
//添加容器事件
let touchStart,
touchEnd,
touchMove,
touchResize,
touchRotate,
touchDelete,
centerPoint,
prevAngle,
touchX = 0,
touchY = 0,
startX = 0,
startY = 0,
prevScale = 100;
const eleReal = ele,
eleConfig = { translate: `0,0,0`, scale: 100, rotate: 0 },
w = eleReal.offsetWidth,
h = eleReal.offsetHeight;
eleReal.style.width = `${w}px`;
eleReal.style.height = `${h}px`;
setConfig(eleReal, eleConfig);
eleReal.style.position = "absolute";
eleReal.style.top = eleReal.style.left = "50%";
eleReal.style.margin = `-${h / 2 + config.padding}px 0 0 -${w / 2 + config.padding}px`;
eleReal.style.padding = `${config.padding}px`;
//选中元素
touchStart = event => {
event.preventDefault();
if (event.target.className.indexOf("pd_child") < 0) {
startX = event.changedTouches[0].pageX - touchX;
startY = event.changedTouches[0].pageY - touchY;
event.target.style.transform = "scale(1.04)";
config.callback && config.callback({ type: "choose", obj: event.target.parentElement });
}
};
touchEnd = event => {
if (event.target.className.indexOf("pd_child") < 0) event.target.style.transform = "scale(1)";
};
//移动事件
touchMove = event => {
if (event.touches.length < 2 && event.target.className.indexOf("pd_child") < 0) {
const nowX = event.changedTouches[0].pageX,
nowY = event.changedTouches[0].pageY,
w = event.target.getBoundingClientRect().width,
h = event.target.getBoundingClientRect().height,
icon = event.target.parentElement.querySelectorAll(`.Pd-ImgTransit-btn`)[0].getBoundingClientRect(),
iconW = icon.width / 2,
getBounding = event.target.parentElement.parentElement.getBoundingClientRect(),
parentBox = {
width: config.bounds ? getBounding.width + config.outBounds : getBounding.width,
height: config.bounds ? getBounding.height + config.outBounds : getBounding.height,
};
touchX = nowX - startX;
touchY = nowY - startY;
if (config.bounds) {
if (Math.abs(touchX) >= parentBox.width / 2 - w / 2 - iconW) {
if (touchX < 0) {
touchX = -1 * (parentBox.width / 2 - w / 2 - iconW);
} else {
touchX = parentBox.width / 2 - w / 2 - iconW;
}
}
if (Math.abs(touchY) >= parentBox.height / 2 - h / 2 - iconW) {
if (touchY < 0) {
touchY = -1 * (parentBox.height / 2 - h / 2 - iconW);
} else {
touchY = parentBox.height / 2 - h / 2 - iconW;
}
}
}
eleConfig.translate = `${touchX}px,${touchY}px,0`;
setConfig(eleReal, eleConfig);
config.callback && config.callback({ type: "move", obj: eleReal });
}
};
//缩放事件
touchResize = event => {
event.stopImmediatePropagation();
event.preventDefault();
const x = event.changedTouches[0].pageX - eleReal.getBoundingClientRect().left;
if (x > 0 && eleConfig.scale > config.scale.min) eleConfig.scale -= config.scale.rate;
if (x < 0 && eleConfig.scale < config.scale.max) eleConfig.scale += config.scale.rate;
if (event.touches.length >= 2) {
if (config.scale.enable) {
prevScale = event.scale * 100;
eleConfig.scale = prevScale;
}
if (config.rotate.enable) eleConfig.rotate = event.rotation;
}
setConfig(eleReal, eleConfig);
config.callback && config.callback({ type: "resize", obj: eleReal });
};
//旋转事件
touchRotate = event => {
event.stopImmediatePropagation();
event.preventDefault();
const angle = Math.atan2(event.changedTouches[0].pageY - centerPoint.y, event.changedTouches[0].pageX - centerPoint.x);
eleConfig.rotate = Math.floor(((angle - prevAngle) * 180) / Math.PI) * config.rotate.rate;
setConfig(eleReal, eleConfig);
config.callback && config.callback({ type: "rotate", obj: eleReal });
};
//删除事件
touchDelete = event => {
event.stopImmediatePropagation();
event.preventDefault();
eleConfig.translate = "0,0,0";
eleConfig.rotate = 0;
eleConfig.scale = 100;
setConfig(eleReal, eleConfig);
eleReal.style.display = "none";
config.callback && config.callback({ type: "delete", obj: eleReal });
};
//绑定所有操作
eleReal.addEventListener("touchstart", touchStart);
eleReal.addEventListener("touchend", touchEnd);
eleReal.addEventListener("touchmove", touchMove);
if (config.scale.enable && config.rotate.enable && config.Gesture) {
setGesture(eleReal).gesturemove = e => {
touchResize(e);
touchRotate(e);
};
}
if (config.icon && config.scale.enable) eleReal.querySelectorAll(`.Pd-resize`)[0].addEventListener("touchmove", touchResize);
if (config.icon && config.rotate.enable) {
eleReal.querySelectorAll(`.Pd-rotate`)[0].addEventListener("touchstart", event => {
centerPoint = getCenterPoint(eleReal);
prevAngle = Math.atan2(event.changedTouches[0].pageY - centerPoint.y, event.changedTouches[0].pageX - centerPoint.x) - (eleConfig.rotate * Math.PI) / 180;
});
eleReal.querySelectorAll(`.Pd-rotate`)[0].addEventListener("touchmove", touchRotate);
}
if (config.icon && config.delete) eleReal.querySelectorAll(`.Pd-delete`)[0].addEventListener("touchstart", touchDelete);
};
//隐藏操作按钮
const hideBtn = () => {
const allCon = TransitBox.querySelectorAll(`.Pd-ImgTransit`),
allBtn = TransitBox.querySelectorAll(`.Pd-ImgTransit-btn`);
for (let a of allCon) {
a.style.border = "none";
a.style.zIndex = 1;
}
for (let a of allBtn) a.style.opacity = 0;
};
//显示操作按钮
const showBtn = tag => {
const curBtn = tag.querySelectorAll(`.Pd-ImgTransit-btn`);
for (let a of curBtn) {
a.style.opacity = 1;
if (config.border) tag.style.border = "2px dashed white";
tag.style.zIndex = 2;
}
};
// 初始化
for (let the of imgs) {
let btn = "";
if (config.icon) {
config.scale.enable && (btn += icon.resize);
config.rotate.enable && (btn += icon.rotate);
config.delete && (btn += icon.delete);
}
const TransitElement = document.createElement("div");
TransitElement.className = `Pd-ImgTransit pd_child_${the.alt}`;
TransitElement.style.position = "absolute";
the.style.transition = "transform .4s ease-in";
TransitElement.innerHTML = btn;
TransitElement.appendChild(the);
TransitBox.appendChild(TransitElement);
addEvent(TransitElement);
}
hideBtn();
//显示当前按钮
TransitBox.addEventListener("touchstart", event => {
hideBtn();
if (event.target !== TransitBox && event.target.className.indexOf("pd_child") < 0) {
config.icon ? showBtn(event.target.parentElement) : hideBtn();
event.target.zIndex = 1;
}
});
return this;
}
//微信SDK
wxSDK(options) {
let config = {
//相关接口地址(类型:字符串)
apiUrl: null,
//分享sdk版本
sdk: "https://res.wx.qq.com/open/js/jweixin-1.6.0.js",
//分享标题(类型:字符串或数组)
title: ["分享至朋友圈", "分享至好友"],
//分享描述(类型:字符串)
desc: "万事皆虚,万物皆允",
//分享图(类型:字符串或数组)
shareIcon: `https://${Alphabet[3]}/share_ico.jpg`,
//分享链接(类型:字符串或数组)
shareLinks: w.location.href,
//调试(类型:布尔)
debug: !1,
//微信jsApiList(类型:数组)
jsApiList: null,
//开放标签列表(类型:数组)
openTagList: null,
//回调方法
callback: {
//分享就绪(类型:方法)
ready: null,
//分享成功(类型:方法)
success: null,
//分享失败或取消(类型:方法)
error: null,
},
};
config = this.extend(config, options);
const scriptTag = document.createElement("script");
let { apiUrl, sdk, title, desc, shareLinks, debug, jsApiList, openTagList, callback, shareIcon } = config;
scriptTag.id = "Pd_share";
scriptTag.src = `${sdk}?${new Date().getTime()}`;
document.querySelector(`#Pd_share`) && document.querySelector(`#Pd_share`).remove();
document.body.appendChild(scriptTag);
let hasIcon = !1;
const isObj = con => {
if (typeof con === "object") {
return !0;
} else {
return !1;
}
};
document.querySelectorAll(`link`).forEach(tag => {
if (tag.getAttribute(`rel`) == "shortcut icon") {
hasIcon = !0;
shareIcon = tag.href;
}
});
if (hasIcon) {
const link = document.createElement(`link`);
link.rel = "shortcut icon";
link.href = isObj(shareIcon) ? shareIcon[0] : shareIcon;
link.type = "image/x-icon";
document.querySelector(`head`).appendChild(link);
}
let jsApiLists = ["onMenuShareTimeline", "onMenuShareAppMessage", "updateTimelineShareData", "updateAppMessageShareData"];
let openTagLists = ["wx-open-launch-app"];
if (jsApiList) {
jsApiList.map(e => {
jsApiLists.push(e);
});
}
if (openTagList) {
openTagList.map(e => {
openTagLists.push(e);
});
}
const timeLine = {
title: isObj(title) ? title[0] : title,
link: isObj(shareLinks) ? shareLinks[0] : shareLinks,
imgUrl: isObj(shareIcon) ? shareIcon[0] : shareIcon,
},
friend = {
title: isObj(title) ? title[1] : title,
link: isObj(shareLinks) ? shareLinks[1] : shareLinks,
imgUrl: isObj(shareIcon) ? shareIcon[1] : shareIcon,
desc,
};
const success = res => {
const { appId, timestamp, nonceStr, signature } = res;
wx.config({ debug, appId, timestamp, nonceStr, signature, jsApiList: jsApiLists, openTagList: openTagLists });
wx.ready(() => {
new Promise(next => {
if (wx.onMenuShareTimeline) {
const { title, link, imgUrl } = timeLine;
const { success, error } = callback;
wx.onMenuShareTimeline({ title, link, imgUrl, success, error });
} else {
const { title, link, imgUrl } = timeLine;
const { success, error } = callback;
wx.updateTimelineShareData({ title, link, imgUrl, success, error });
}
if (wx.onMenuShareAppMessage) {
const { title, link, imgUrl, desc } = friend;
const { success, error } = callback;
wx.onMenuShareAppMessage({ title, desc, link, imgUrl, success, error });
} else {
const { title, link, imgUrl, desc } = friend;
const { success, error } = callback;
wx.updateAppMessageShareData({ title, desc, link, imgUrl, success, error });
}
next();
})
.then(callback.ready)
.catch(err => {
console.error(`[${Alphabet[8]} - wxSDK]`, err);
});
});
};
scriptTag.onload = () => {
this.ajax({ url: `${apiUrl}${encodeURIComponent(w.location.href.split(`#`)[0])}` }).then(success);
};
return this;
}
//懒加载
LazyLoad(options) {
let config = {
//缺省尺寸
width: 100,
height: 100,
//缺省图标(类型:字符串)
icon: icoConfig.load,
};
config = this.extend(config, options);
const imgArr = this.child(`img`).get,
{ width, height, icon } = config;
let cur = 0,
lazyArr = [];
//遍历所有图片
for (let img of imgArr) {
if (img.dataset.src) {
img.width = width;
img.height = height;
img.style.background = `url("${icon}") no-repeat center,black`;
img.style.backgroundSize = `20%`;
lazyArr.push(img);
}
}
//进入视图
const inView = obj => {
if (obj.getBoundingClientRect().y - w.innerHeight < 0) return obj;
return !1;
};
//检测图片状态
const checker = () => {
lazyArr.forEach(img => {
if (inView(img) && !img.src && img.complete) {
img.src = inView(img).dataset.src;
img.style.transition = "all .8s ease";
img.onload = () => {
let newWidth = Number(img.dataset.width) || img.naturalWidth,
newHeight = Number(img.dataset.height) || img.naturalHeight;
if (img.dataset.width) newHeight = (newWidth / img.naturalWidth) * img.naturalHeight;
if (img.dataset.height) newWidth = (newHeight / img.naturalHeight) * img.naturalWidth;
img.width = newWidth;
img.height = newHeight;
img.removeAttribute("data-src");
img.style.background = null;
img.dataset.width && img.removeAttribute("data-width");
img.dataset.height && img.removeAttribute("data-height");
cur++;
if (cur == lazyArr.length) w.removeEventListener("scroll", checker);
img.addEventListener("transitionend", () => {
img.style.transition = null;
});
};
img.onerror = () => {
console.error(`[${Alphabet[8]} - LazyLoad] 发生错误:${img.src}`);
cur++;
};
}
});
};
//页面滚动事件
w.addEventListener("scroll", checker);
checker();
return this;
}
//上传OSS
ossUpload(options) {
let config = {
// 阿里云账号AccessId(类型:字符串)
AccessId: null,
// 阿里云账号AccessKey(类型:字符串)
AccessKey: null,
// OSS Bucket 外网域名(类型:字符串)
Endpoint: null,
// 文件大小限制(类型整数单位MB)
maxSize: 2,
};
config = this.extend(config, options);
const that = this;
const { AccessId, AccessKey, Endpoint, maxSize } = config;
// 引用检测
try {
Crypto;
} catch (err) {
return console.error(`[${Alphabet[8]}] 缺少Crypto工具方法`, err);
}
if (!Crypto.HMAC) return console.error(`[${Alphabet[8]}] 缺少Crypto工具的HMAC方法`);
if (!Crypto.SHA1) return console.error(`[${Alphabet[8]}] 缺少Crypto工具的SHA1方法`);
// 配置检测
if (!AccessId) return console.error(`[${Alphabet[8]}] 缺失阿里云账号AccessId`);
if (!AccessKey) return console.error(`[${Alphabet[8]}] 缺失阿里云账号AccessKey`);
if (!Endpoint) return console.error(`[${Alphabet[8]}] 缺失OSS Bucket 外网域名`);
// 自动补零
const padThat = str => {
return str.toString().padStart(2, 0);
};
// 客户端签名
const policyText = {
// 获取失效时间
expiration: (function () {
let date;
const Year = new Date().getFullYear(),
Month = new Date().getMonth() + 1,
Day = new Date().getDate(),
Hours = new Date().getHours();
date = `${Year}-${padThat(Month)}-${padThat(Day)}T${padThat(Hours)}:00:00.000Z`;
return date;
})(),
conditions: [
["content-length-range", 0, maxSize * 1024 * 1024], // 设置上传文件的大小限制
],
};
// 获取拓展名
const getSuffix = filename => {
return filename.substring(filename.lastIndexOf("."));
};
// 上传文件
const uploadFile = obj => {
let {
// 待上传文件(类型:二进制)
fileObj = null,
// 文件名(类型:字符串,默认生成随机文件名)
fileName = that.guid(),
// 上传目录名称(类型:字符串,默认根目录)
dirName = "",
// 上传请求头(类型:对象)
headers = null,
// 是否开启同步上传(类型:布尔值)
async = !0,
// 上传中回调(类型:方法;返回类型:数字)
progress = null,
} = obj;
const policyBase64 = OSSBase64.encode(JSON.stringify(policyText)),
bytes = Crypto.HMAC(Crypto.SHA1, policyBase64, AccessKey, { asBytes: !0 }),
signature = Crypto.util.bytesToBase64(bytes);
return new Promise((resolve, reject) => {
if (fileObj) {
if (fileObj.size > maxSize * 1024 * 1024) {
reject("overSize");
return console.warn(`[${Alphabet[9]}] 文件大小超出最大限制`);
} else {
const formData = new FormData();
formData.append("name", `${fileName}${getSuffix(fileObj.name)}`);
const copyFile = new File([fileObj], `${fileName}${getSuffix(fileObj.name)}`, { type: fileObj.type });
fileObj = copyFile;
formData.append("key", dirName ? `${dirName}/\${filename\}` : "${filename}");
formData.append("policy", policyBase64);
formData.append("OSSAccessKeyId", AccessId);
formData.append("success_action_status", "200");
formData.append("signature", signature);
formData.append("file", fileObj);
that
.ajax({
url: Endpoint,
type: "post",
headers,
async,
dataType: "form",
data: formData,
progress,
})
.then(() => {
resolve({
code: 200,
msg: "上传成功",
url: (function () {
let url;
if (dirName != "") {
url = `${Endpoint}/${dirName}/${fileObj.name}`;
} else {
url = `${Endpoint}/${fileObj.name}`;
}
return url;
})(),
});
})
.catch(reject);
}
} else {
reject("noFile");
return console.warn(`[${Alphabet[9]}] 请选择需要上传的文件`);
}
});
};
this.start = uploadFile;
return this;
}
};
};
const Pandora = class extends PandoraJs(PandoraEX) {
constructor(obj = null) {
super(obj);
}
};
w.Pandora = Pandora;
// 判断是否引用了JQuery或者Zepto
if (!w.$) {
w.$ = obj => {
return new Pandora(obj);
};
}
})(window || global || self || this);