;(w => { 'use strict' const OSSBase64 = require('./src/base64.js') const icoConfig = require('./src/icoConfig.json') // 缺省字段 const Alphabet = ['active', 'local', 'localhost', '', '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 } // TODO: 静态路由 (二级目录;DOM虚拟化;完善生命周期;) Router(options) { let config = { // 路由路径集合(类型:数组) routes: null, } config = this.extend(config, options) const { routes } = config // 把路由路径集合中的path放入数组 let routePath = [] for (let route of routes) routePath.push(route.path) // 渲染路由 const render = path => { // 创建影子节点 const shadow = document.createElement('div') // 当前路由集合 const { component, callback } = routes[routePath.indexOf(path)] shadow.attachShadow({ mode: 'open', serializable: true }) // 读取页面 fetch(component) .then(res => { return res.text() }) .then(html => { shadow.innerHTML = html this.empty() // 把影子节点转换为真实节点,并插入到容器中 this.append(shadow.getHTML()) // 覆写A标签 const a = document.querySelectorAll('a') for (let e of a) { e.onclick = e => { const href = e.target.getAttribute('href') if (href) { if (!/http|https|ftp|ftps|mailto|javascript/.test(href) && href.indexOf('#') < 0) { // 阻止默认事件 e.preventDefault() this.to(href) } } } } callback && callback(w.history.state) }) } // 路由切换 this.to = path => { // 判断路由是否带有参数 let query = null if (path.indexOf('?') > -1) { query = path.split('?')[1] path = path.split('?')[0] // 把参数转换为对象 let obj = {} query.split('&').map(e => { obj[e.split('=')[0]] = e.split('=')[1] }) query = obj } if (routePath.includes(path)) { if (query) { // 把参数转换为字符串 const queryStr = Object.keys(query).map(key => { return `${key}=${query[key]}` }) w.history.replaceState(query, null, `${path}?${queryStr.join('&')}`) } else { w.history.pushState(query, null, path) } render(path) } else { console.error(`[${Alphabet[8]} - Router] ${path} 路由不存在!`) } } this.to(w.location.href.split(w.location.origin)[1]) 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', ``) 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', "
") 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 `` } 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://src.pandorastudio.cn/favicon.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] : w.location.href.split(`#`)[0], imgUrl: isObj(shareIcon) ? shareIcon[0] : shareIcon, }, friend = { title: isObj(title) ? title[1] : title, link: isObj(shareLinks) ? shareLinks[1] : w.location.href.split(`#`)[0], 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.updateTimelineShareData) { const { title, link, imgUrl } = timeLine const { success, error } = callback wx.updateTimelineShareData({ title, link, imgUrl, success, error }) } if (wx.updateAppMessageShareData) { 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}${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)