forked from githubdulong/Script
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDmYY.scriptable
More file actions
12 lines (11 loc) · 42.9 KB
/
Copy pathDmYY.scriptable
File metadata and controls
12 lines (11 loc) · 42.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
{
"always_run_in_app" : false,
"icon" : {
"color" : "teal",
"glyph" : "cogs"
},
"name" : "DmYY",
"script" : "\n\/*\n * Author: 2Ya\n * Github: https:\/\/github.com\/dompling\n *\/\n\nclass DmYY {\n constructor(arg) {\n this.arg = arg\n try {\n this.init()\n } catch (error) {\n console.log(error)\n }\n this.isNight = Device.isUsingDarkAppearance()\n }\n\n _actions = {}\n BACKGROUND_NIGHT_KEY\n widgetColor\n backGroundColor\n useBoxJS = true\n isNight\n _actionsIcon = {}\n\n \/\/ 获取 Request 对象\n getRequest = (url = '') => {\n return new Request(url)\n }\n\n \/\/ 发起请求\n http = async (options = { headers: {}, url: '' }, type = 'JSON') => {\n try {\n let request\n if (type !== 'IMG') {\n request = this.getRequest()\n Object.keys(options).forEach((key) => {\n request[key] = options[key]\n })\n request.headers = { ...this.defaultHeaders, ...options.headers }\n } else {\n request = this.getRequest(options.url)\n return (await request.loadImage()) || SFSymbol.named('photo').image\n }\n if (type === 'JSON') {\n return await request.loadJSON()\n }\n if (type === 'STRING') {\n return await request.loadString()\n }\n return await request.loadJSON()\n } catch (e) {\n console.log('error:' + e)\n if (type === 'IMG') return SFSymbol.named('photo').image\n }\n }\n\n \/\/request 接口请求\n $request = {\n get: async (url = '', options = {}, type = 'JSON') => {\n let params = { ...options, method: 'GET' }\n if (typeof url === 'object') {\n params = { ...params, ...url }\n } else {\n params.url = url\n }\n let _type = type\n if (typeof options === 'string') _type = options\n return await this.http(params, _type)\n },\n post: async (url = '', options = {}, type = 'JSON') => {\n let params = { ...options, method: 'POST' }\n if (typeof url === 'object') {\n params = { ...params, ...url }\n } else {\n params.url = url\n }\n let _type = type\n if (typeof options === 'string') _type = options\n return await this.http(params, _type)\n },\n }\n\n \/\/ 获取 boxJS 缓存\n getCache = async (key = '', notify = true) => {\n try {\n let url = 'http:\/\/' + this.prefix + '\/query\/boxdata'\n if (key) url = 'http:\/\/' + this.prefix + '\/query\/data\/' + key\n const boxdata = await this.$request.get(\n url,\n key ? { timeoutInterval: 1 } : {}\n )\n if (boxdata.val) return boxdata.val\n return boxdata.datas\n } catch (e) {\n if (notify)\n await this.notify(\n `${this.name} - BoxJS 数据读取失败`,\n '请检查 BoxJS 域名是否为代理复写的域名,如(boxjs.net 或 boxjs.com)。\\n若没有配置 BoxJS 相关模块,请点击通知查看教程',\n 'https:\/\/chavyleung.gitbook.io\/boxjs\/awesome\/videos'\n )\n return false\n }\n }\n\n transforJSON = (str) => {\n if (typeof str == 'string') {\n try {\n return JSON.parse(str)\n } catch (e) {\n console.log(e)\n return str\n }\n }\n console.log('It is not a string!')\n }\n\n \/\/ 选择图片并缓存\n chooseImg = async () => {\n return await Photos.fromLibrary()\n }\n\n \/\/ 设置 widget 背景图片\n getWidgetBackgroundImage = async (widget) => {\n const backgroundImage = this.getBackgroundImage()\n if (backgroundImage) {\n const opacity = Device.isUsingDarkAppearance()\n ? Number(this.settings.darkOpacity)\n : Number(this.settings.lightOpacity)\n widget.backgroundImage = await this.shadowImage(\n backgroundImage,\n '#000',\n opacity\n )\n return true\n } else {\n if (this.backGroundColor.colors) {\n widget.backgroundGradient = this.backGroundColor\n } else {\n widget.backgroundColor = this.backGroundColor\n }\n return false\n }\n }\n\n \/**\n * 验证图片尺寸: 图片像素超过 1000 左右的时候会导致背景无法加载\n * @param img Image\n *\/\n verifyImage = async (img) => {\n try {\n const { width, height } = img.size\n const direct = true\n if (width > 1000) {\n const options = ['取消', '打开图像处理']\n const message =\n '您的图片像素为' +\n width +\n ' x ' +\n height +\n '\\n' +\n '请将图片' +\n (direct ? '宽度' : '高度') +\n '调整到 1000 以下\\n' +\n (!direct ? '宽度' : '高度') +\n '自动适应'\n const index = await this.generateAlert(message, options)\n if (index === 1)\n Safari.openInApp('https:\/\/www.sojson.com\/image\/change.html', false)\n return false\n }\n return true\n } catch (e) {\n return false\n }\n }\n\n \/**\n * 获取截图中的组件剪裁图\n * 可用作透明背景\n * 返回图片image对象\n * 代码改自:https:\/\/gist.github.com\/mzeryck\/3a97ccd1e059b3afa3c6666d27a496c9\n * @param {string} title 开始处理前提示用户截图的信息,可选(适合用在组件自定义透明背景时提示)\n *\/\n async getWidgetScreenShot(title = null) {\n \/\/ Crop an image into the specified rect.\n function cropImage(img, rect) {\n let draw = new DrawContext()\n draw.size = new Size(rect.width, rect.height)\n\n draw.drawImageAtPoint(img, new Point(-rect.x, -rect.y))\n return draw.getImage()\n }\n\n \/\/ Pixel sizes and positions for widgets on all supported phones.\n function phoneSizes() {\n return {\n \/\/ 12 Pro Max\n 2778: {\n small: 510,\n medium: 1092,\n large: 1146,\n left: 96,\n right: 678,\n top: 246,\n middle: 882,\n bottom: 1518,\n },\n\n \/\/ 12 and 12 Pro\n 2532: {\n small: 474,\n medium: 1014,\n large: 1062,\n left: 78,\n right: 618,\n top: 231,\n middle: 819,\n bottom: 1407,\n },\n\n \/\/ 11 Pro Max, XS Max\n 2688: {\n small: 507,\n medium: 1080,\n large: 1137,\n left: 81,\n right: 654,\n top: 228,\n middle: 858,\n bottom: 1488,\n },\n\n \/\/ 11, XR\n 1792: {\n small: 338,\n medium: 720,\n large: 758,\n left: 54,\n right: 436,\n top: 160,\n middle: 580,\n bottom: 1000,\n },\n\n \/\/ 11 Pro, XS, X, 12 mini\n 2436: {\n x: {\n small: 465,\n medium: 987,\n large: 1035,\n left: 69,\n right: 591,\n top: 213,\n middle: 783,\n bottom: 1353,\n },\n\n mini: {\n small: 465,\n medium: 987,\n large: 1035,\n left: 69,\n right: 591,\n top: 231,\n middle: 801,\n bottom: 1371,\n },\n },\n\n \/\/ Plus phones\n 2208: {\n small: 471,\n medium: 1044,\n large: 1071,\n left: 99,\n right: 672,\n top: 114,\n middle: 696,\n bottom: 1278,\n },\n\n \/\/ SE2 and 6\/6S\/7\/8\n 1334: {\n small: 296,\n medium: 642,\n large: 648,\n left: 54,\n right: 400,\n top: 60,\n middle: 412,\n bottom: 764,\n },\n\n \/\/ SE1\n 1136: {\n small: 282,\n medium: 584,\n large: 622,\n left: 30,\n right: 332,\n top: 59,\n middle: 399,\n bottom: 399,\n },\n\n \/\/ 11 and XR in Display Zoom mode\n 1624: {\n small: 310,\n medium: 658,\n large: 690,\n left: 46,\n right: 394,\n top: 142,\n middle: 522,\n bottom: 902,\n },\n\n \/\/ Plus in Display Zoom mode\n 2001: {\n small: 444,\n medium: 963,\n large: 972,\n left: 81,\n right: 600,\n top: 90,\n middle: 618,\n bottom: 1146,\n },\n }\n }\n\n let message =\n title || '开始之前,请先前往桌面,截取空白界面的截图。然后回来继续'\n let exitOptions = ['我已截图', '前去截图 >']\n let shouldExit = await this.generateAlert(message, exitOptions)\n if (shouldExit) return\n\n \/\/ Get screenshot and determine phone size.\n let img = await Photos.fromLibrary()\n let height = img.size.height\n let phone = phoneSizes()[height]\n if (!phone) {\n message = '好像您选择的照片不是正确的截图,请先前往桌面'\n await this.generateAlert(message, ['我已知晓'])\n return\n }\n\n \/\/ Extra setup needed for 2436-sized phones.\n if (height === 2436) {\n const files = this.FILE_MGR_LOCAL\n let cacheName = 'mz-phone-type'\n let cachePath = files.joinPath(files.libraryDirectory(), cacheName)\n\n \/\/ If we already cached the phone size, load it.\n if (files.fileExists(cachePath)) {\n let typeString = files.readString(cachePath)\n phone = phone[typeString]\n \/\/ Otherwise, prompt the user.\n } else {\n message = '您的📱型号是?'\n let types = ['iPhone 12 mini', 'iPhone 11 Pro, XS, or X']\n let typeIndex = await this.generateAlert(message, types)\n let type = typeIndex === 0 ? 'mini' : 'x'\n phone = phone[type]\n files.writeString(cachePath, type)\n }\n }\n\n \/\/ Prompt for widget size and position.\n message = '截图中要设置透明背景组件的尺寸类型是?'\n let sizes = ['小尺寸', '中尺寸', '大尺寸']\n let size = await this.generateAlert(message, sizes)\n let widgetSize = sizes[size]\n\n message = '要设置透明背景的小组件在哪个位置?'\n message +=\n height === 1136\n ? ' (备注:当前设备只支持两行小组件,所以下边选项中的「中间」和「底部」的选项是一致的)'\n : ''\n\n \/\/ Determine image crop based on phone size.\n let crop = { w: '', h: '', x: '', y: '' }\n if (widgetSize === '小尺寸') {\n crop.w = phone.small\n crop.h = phone.small\n let positions = [\n '左上角',\n '右上角',\n '中间左',\n '中间右',\n '左下角',\n '右下角',\n ]\n let _posotions = [\n 'Top left',\n 'Top right',\n 'Middle left',\n 'Middle right',\n 'Bottom left',\n 'Bottom right',\n ]\n let position = await this.generateAlert(message, positions)\n\n \/\/ Convert the two words into two keys for the phone size dictionary.\n let keys = _posotions[position].toLowerCase().split(' ')\n crop.y = phone[keys[0]]\n crop.x = phone[keys[1]]\n } else if (widgetSize === '中尺寸') {\n crop.w = phone.medium\n crop.h = phone.small\n\n \/\/ Medium and large widgets have a fixed x-value.\n crop.x = phone.left\n let positions = ['顶部', '中间', '底部']\n let _positions = ['Top', 'Middle', 'Bottom']\n let position = await this.generateAlert(message, positions)\n let key = _positions[position].toLowerCase()\n crop.y = phone[key]\n } else if (widgetSize === '大尺寸') {\n crop.w = phone.medium\n crop.h = phone.large\n crop.x = phone.left\n let positions = ['顶部', '底部']\n let position = await this.generateAlert(message, positions)\n\n \/\/ Large widgets at the bottom have the \"middle\" y-value.\n crop.y = position ? phone.middle : phone.top\n }\n\n \/\/ Crop image and finalize the widget.\n return cropImage(img, new Rect(crop.x, crop.y, crop.w, crop.h))\n }\n\n setLightAndDark = async (title, desc, val) => {\n try {\n const a = new Alert()\n a.title = title\n a.message = desc\n a.addTextField('', `${this.settings[val]}`)\n a.addAction('确定')\n a.addCancelAction('取消')\n const id = await a.presentAlert()\n if (id === -1) return\n this.settings[val] = a.textFieldValue(0)\n this.saveSettings()\n } catch (e) {\n console.log(e)\n }\n }\n\n \/**\n * 弹出输入框\n * @param title 标题\n * @param desc 描述\n * @param opt 属性\n * @returns {Promise<void>}\n *\/\n setAlertInput = async (title, desc, opt = {}, isSave = true) => {\n const a = new Alert()\n a.title = title\n a.message = !desc ? '' : desc\n Object.keys(opt).forEach((key) => {\n a.addTextField(opt[key], this.settings[key])\n })\n a.addAction('确定')\n a.addCancelAction('取消')\n const id = await a.presentAlert()\n if (id === -1) return\n const data = {}\n Object.keys(opt).forEach((key, index) => {\n data[key] = a.textFieldValue(index)\n })\n \/\/ 保存到本地\n if (isSave) {\n this.settings = { ...this.settings, ...data }\n return this.saveSettings()\n }\n return data\n }\n\n \/**\n * 设置当前项目的 boxJS 缓存\n * @param opt key value\n * @returns {Promise<void>}\n *\/\n setCacheBoxJSData = async (opt = {}) => {\n const options = ['取消', '确定']\n const message = '代理缓存仅支持 BoxJS 相关的代理!'\n const index = await this.generateAlert(message, options)\n if (index === 0) return\n try {\n const boxJSData = await this.getCache()\n Object.keys(opt).forEach((key) => {\n this.settings[key] = boxJSData[opt[key]] || ''\n })\n \/\/ 保存到本地\n this.saveSettings()\n } catch (e) {\n console.log(e)\n this.notify(\n this.name,\n 'BoxJS 缓存读取失败!点击查看相关教程',\n 'https:\/\/chavyleung.gitbook.io\/boxjs\/awesome\/videos'\n )\n }\n }\n\n \/**\n * 设置组件内容\n * @returns {Promise<void>}\n *\/\n setWidgetConfig = async () => {\n const table = new UITable()\n table.showSeparators = true\n await this.renderDmYYTables(table)\n await table.present()\n }\n\n async preferences(table, arr, outfit) {\n let header = new UITableRow()\n let heading = header.addText(outfit)\n heading.titleFont = Font.mediumSystemFont(17)\n heading.centerAligned()\n table.addRow(header)\n for (const item of arr) {\n const row = new UITableRow()\n row.dismissOnSelect = !!item.dismissOnSelect\n if (item.url) {\n const rowIcon = row.addImageAtURL(item.url)\n rowIcon.widthWeight = 100\n } else {\n const icon = item.icon || {}\n const image = await this.drawTableIcon(\n icon.name,\n icon.color,\n item.cornerWidth\n )\n const imageCell = row.addImage(image)\n imageCell.widthWeight = 100\n }\n let rowTitle = row.addText(item['title'])\n rowTitle.widthWeight = 400\n rowTitle.titleFont = Font.systemFont(16)\n if (this.settings[item.val] || item.val) {\n let valText = row.addText(\n `${this.settings[item.val] || item.val}`.toUpperCase()\n )\n const fontSize = !item.val ? 26 : 16\n valText.widthWeight = 500\n valText.rightAligned()\n valText.titleColor = Color.blue()\n valText.titleFont = Font.mediumSystemFont(fontSize)\n } else {\n const imgCell = UITableCell.imageAtURL(\n 'https:\/\/gitee.com\/scriptableJS\/Scriptable\/raw\/master\/images\/more.png'\n )\n imgCell.rightAligned()\n imgCell.widthWeight = 500\n row.addCell(imgCell)\n }\n\n row.onSelect = item.onClick\n ? async () => {\n try {\n await item.onClick(item, table)\n } catch (e) {\n console.log(e)\n }\n }\n : async () => {\n if (item.type == 'input') {\n await this.setLightAndDark(\n item['title'],\n item['desc'],\n item['val']\n )\n } else if (item.type == 'setBackground') {\n const backImage = await this.getWidgetScreenShot()\n if (backImage) {\n await this.setBackgroundImage(backImage, true)\n await this.setBackgroundNightImage(backImage, true)\n }\n } else if (item.type == 'removeBackground') {\n const options = ['取消', '清空']\n const message = '该操作不可逆,会清空所有背景图片!'\n const index = await this.generateAlert(message, options)\n if (index === 0) return\n await this.setBackgroundImage(false, true)\n await this.setBackgroundNightImage(false, true)\n } else {\n const backImage = await this.chooseImg()\n if (!backImage || !(await this.verifyImage(backImage))) return\n if (item.type == 'setDayBackground')\n await this.setBackgroundImage(backImage, true)\n if (item.type == 'setNightBackground')\n await this.setBackgroundNightImage(backImage, true)\n }\n await this.renderDmYYTables(table)\n }\n table.addRow(row)\n }\n table.reload()\n }\n\n drawTableIcon = async (\n icon = 'square.grid.2x2',\n color = '#e8e8e8',\n cornerWidth = 42\n ) => {\n const sfi = SFSymbol.named(icon)\n sfi.applyFont(Font.mediumSystemFont(30))\n const imgData = Data.fromPNG(sfi.image).toBase64String()\n const html = `\n <img id=\"sourceImg\" src=\"data:image\/png;base64,${imgData}\" \/>\n <img id=\"silhouetteImg\" src=\"\" \/>\n <canvas id=\"mainCanvas\" \/>\n `\n const js = `\n var canvas = document.createElement(\"canvas\");\n var sourceImg = document.getElementById(\"sourceImg\");\n var silhouetteImg = document.getElementById(\"silhouetteImg\");\n var ctx = canvas.getContext('2d');\n var size = sourceImg.width > sourceImg.height ? sourceImg.width : sourceImg.height;\n canvas.width = size;\n canvas.height = size;\n ctx.drawImage(sourceImg, (canvas.width - sourceImg.width) \/ 2, (canvas.height - sourceImg.height) \/ 2);\n var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);\n var pix = imgData.data;\n \/\/convert the image into a silhouette\n for (var i=0, n = pix.length; i < n; i+= 4){\n \/\/set red to 0\n pix[i] = 255;\n \/\/set green to 0\n pix[i+1] = 255;\n \/\/set blue to 0\n pix[i+2] = 255;\n \/\/retain the alpha value\n pix[i+3] = pix[i+3];\n }\n ctx.putImageData(imgData,0,0);\n silhouetteImg.src = canvas.toDataURL();\n output=canvas.toDataURL()\n `\n\n let wv = new WebView()\n await wv.loadHTML(html)\n const base64Image = await wv.evaluateJavaScript(js)\n const iconImage = await new Request(base64Image).loadImage()\n const size = new Size(160, 160)\n const ctx = new DrawContext()\n ctx.opaque = false\n ctx.respectScreenScale = true\n ctx.size = size\n const path = new Path()\n const rect = new Rect(0, 0, size.width, size.width)\n\n path.addRoundedRect(rect, cornerWidth, cornerWidth)\n path.closeSubpath()\n ctx.setFillColor(new Color(color))\n ctx.addPath(path)\n ctx.fillPath()\n const rate = 36\n const iw = size.width - rate\n const x = (size.width - iw) \/ 2\n ctx.drawImageInRect(iconImage, new Rect(x, x, iw, iw))\n return ctx.getImage()\n }\n\n async renderDmYYTables(table) {\n const basic = [\n {\n icon: { name: 'arrow.clockwise', color: '#1890ff' },\n type: 'input',\n title: '刷新时间',\n desc: '刷新时间仅供参考,具体刷新时间由系统判断,单位:分钟',\n val: 'refreshAfterDate',\n },\n {\n icon: { name: 'photo', color: '#13c2c2' },\n type: 'input',\n title: '白天背景颜色',\n desc: '请自行去网站上搜寻颜色(Hex 颜色)\\n支持渐变色,各颜色之间以英文逗号分隔',\n val: 'lightBgColor',\n },\n {\n icon: { name: 'photo.fill', color: '#52c41a' },\n type: 'input',\n title: '晚上背景颜色',\n desc: '请自行去网站上搜寻颜色(Hex 颜色)\\n支持渐变色,各颜色之间以英文逗号分隔',\n val: 'darkBgColor',\n },\n {\n icon: { name: 'sun.max.fill', color: '#d48806' },\n type: 'input',\n title: '白天字体颜色',\n desc: '请自行去网站上搜寻颜色(Hex 颜色)',\n val: 'lightColor',\n },\n {\n icon: { name: 'moon.stars.fill', color: '#d4b106' },\n type: 'input',\n title: '晚上字体颜色',\n desc: '请自行去网站上搜寻颜色(Hex 颜色)',\n val: 'darkColor',\n },\n ]\n const background = [\n {\n icon: { name: 'text.below.photo', color: '#faad14' },\n type: 'setBackground',\n title: '透明背景设置',\n },\n {\n icon: { name: 'photo.on.rectangle', color: '#fa8c16' },\n type: 'setDayBackground',\n title: '白天背景图片',\n },\n {\n icon: { name: 'photo.fill.on.rectangle.fill', color: '#fa541c' },\n type: 'setNightBackground',\n title: '晚上背景图片',\n },\n {\n icon: { name: 'record.circle', color: '#722ed1' },\n type: 'input',\n title: '白天蒙层透明',\n desc: '完全透明请设置为0',\n val: 'lightOpacity',\n },\n {\n icon: { name: 'record.circle.fill', color: '#eb2f96' },\n type: 'input',\n title: '晚上蒙层透明',\n desc: '完全透明请设置为0',\n val: 'darkOpacity',\n },\n {\n icon: { name: 'clear', color: '#f5222d' },\n type: 'removeBackground',\n title: '清空背景图片',\n },\n ]\n const boxjs = {\n icon: { name: 'shippingbox', color: '#f7bb10' },\n type: 'input',\n title: 'BoxJS 域名',\n desc: '',\n val: 'boxjsDomain',\n }\n if (this.useBoxJS) basic.push(boxjs)\n table.removeAllRows()\n let topRow = new UITableRow()\n topRow.height = 60\n let leftText = topRow.addButton('Github')\n leftText.widthWeight = 0.3\n leftText.onTap = async () => {\n await Safari.openInApp('https:\/\/github.com\/dompling\/Scriptable')\n }\n let centerRow = topRow.addImageAtURL(\n `https:\/\/gitee.com\/anker1209\/image\/raw\/master\/Doraemon\/doraedmon_${Math.floor(Math.random() * 71)}.png` \/\/哆啦A梦跳舞\n )\n centerRow.widthWeight = 0.4\n centerRow.centerAligned()\n centerRow.onTap = async () => {\n await Safari.open('https:\/\/t.me\/+ViT7uEUrIUV0B_iy')\n }\n let rightText = topRow.addButton('重置所有')\n rightText.widthWeight = 0.3\n rightText.rightAligned()\n rightText.onTap = async () => {\n const options = ['取消', '重置']\n const message =\n '该操作不可逆,会清空所有组件配置!重置后请重新打开设置菜单。'\n const index = await this.generateAlert(message, options)\n if (index === 0) return\n this.settings = {}\n await this.setBackgroundImage(false, false)\n this.saveSettings()\n }\n table.addRow(topRow)\n await this.preferences(table, basic, '基础设置')\n await this.preferences(table, background, '背景图片')\n }\n\n init(widgetFamily = config.widgetFamily) {\n \/\/ 组件大小:small,medium,large\n this.widgetFamily = widgetFamily\n this.SETTING_KEY = this.md5(Script.name())\n \/\/用于配置所有的组件相关设置\n\n \/\/ 文件管理器\n \/\/ 提示:缓存数据不要用这个操作,这个是操作源码目录的,缓存建议存放在local temp目录中\n this.FILE_MGR =\n FileManager[\n module.filename.includes('Documents\/iCloud~') ? 'iCloud' : 'local'\n ]()\n \/\/ 本地,用于存储图片等\n this.FILE_MGR_LOCAL = FileManager.local()\n this.BACKGROUND_KEY = this.FILE_MGR_LOCAL.joinPath(\n this.FILE_MGR_LOCAL.documentsDirectory(),\n 'bg_' + this.SETTING_KEY + '.jpg'\n )\n\n this.BACKGROUND_NIGHT_KEY = this.FILE_MGR_LOCAL.joinPath(\n this.FILE_MGR_LOCAL.documentsDirectory(),\n 'bg_' + this.SETTING_KEY + 'night.jpg'\n )\n\n this.settings = this.getSettings()\n this.settings.lightColor = this.settings.lightColor || '#000000'\n this.settings.darkColor = this.settings.darkColor || '#ffffff'\n this.settings.lightBgColor = this.settings.lightBgColor || '#ffffff'\n this.settings.darkBgColor = this.settings.darkBgColor || '#000000'\n this.settings.boxjsDomain = this.settings.boxjsDomain || 'boxjs.net'\n this.settings.refreshAfterDate = this.settings.refreshAfterDate || '30'\n this.settings.lightOpacity = this.settings.lightOpacity || '0.4'\n this.settings.darkOpacity = this.settings.darkOpacity || '0.7'\n this.prefix = this.settings.boxjsDomain\n const lightBgColor = this.getColors(this.settings.lightBgColor)\n const darkBgColor = this.getColors(this.settings.darkBgColor)\n if (lightBgColor.length > 1 || darkBgColor.length > 1) {\n this.backGroundColor = !Device.isUsingDarkAppearance()\n ? this.getBackgroundColor(lightBgColor)\n : this.getBackgroundColor(darkBgColor)\n } else if (lightBgColor.length > 0 && darkBgColor.length > 0) {\n this.backGroundColor = Color.dynamic(\n new Color(this.settings.lightBgColor),\n new Color(this.settings.darkBgColor)\n )\n }\n this.widgetColor = Color.dynamic(\n new Color(this.settings.lightColor),\n new Color(this.settings.darkColor)\n )\n }\n\n getColors = (color = '') => {\n const colors = typeof color === 'string' ? color.split(',') : color\n return colors\n }\n\n getBackgroundColor = (colors) => {\n const locations = []\n const linearColor = new LinearGradient()\n const cLen = colors.length\n linearColor.colors = colors.map((item, index) => {\n locations.push(Math.floor(((index + 1) \/ cLen) * 100) \/ 100)\n return new Color(item, 1)\n })\n linearColor.locations = locations\n return linearColor\n }\n\n \/**\n * 注册点击操作菜单\n * @param {string} name 操作函数名\n * @param {func} func 点击后执行的函数\n *\/\n registerAction(name, func, icon = { name: 'gear', color: '#096dd9' }) {\n this._actions[name] = func.bind(this)\n this._actionsIcon[name] = icon\n }\n\n \/**\n * base64 编码字符串\n * @param {string} str 要编码的字符串\n *\/\n base64Encode(str) {\n const data = Data.fromString(str)\n return data.toBase64String()\n }\n\n \/**\n * base64解码数据 返回字符串\n * @param {string} b64 base64编码的数据\n *\/\n base64Decode(b64) {\n const data = Data.fromBase64String(b64)\n return data.toRawString()\n }\n\n \/**\n * md5 加密字符串\n * @param {string} str 要加密成md5的数据\n *\/\n md5(str) {\n function d(n, t) {\n var r = (65535 & n) + (65535 & t)\n return (((n >> 16) + (t >> 16) + (r >> 16)) << 16) | (65535 & r)\n }\n\n function f(n, t, r, e, o, u) {\n return d(((c = d(d(t, n), d(e, u))) << (f = o)) | (c >>> (32 - f)), r)\n var c, f\n }\n\n function l(n, t, r, e, o, u, c) {\n return f((t & r) | (~t & e), n, t, o, u, c)\n }\n\n function v(n, t, r, e, o, u, c) {\n return f((t & e) | (r & ~e), n, t, o, u, c)\n }\n\n function g(n, t, r, e, o, u, c) {\n return f(t ^ r ^ e, n, t, o, u, c)\n }\n\n function m(n, t, r, e, o, u, c) {\n return f(r ^ (t | ~e), n, t, o, u, c)\n }\n\n function i(n, t) {\n var r, e, o, u\n ;(n[t >> 5] |= 128 << t % 32), (n[14 + (((t + 64) >>> 9) << 4)] = t)\n for (\n var c = 1732584193,\n f = -271733879,\n i = -1732584194,\n a = 271733878,\n h = 0;\n h < n.length;\n h += 16\n )\n (c = l((r = c), (e = f), (o = i), (u = a), n[h], 7, -680876936)),\n (a = l(a, c, f, i, n[h + 1], 12, -389564586)),\n (i = l(i, a, c, f, n[h + 2], 17, 606105819)),\n (f = l(f, i, a, c, n[h + 3], 22, -1044525330)),\n (c = l(c, f, i, a, n[h + 4], 7, -176418897)),\n (a = l(a, c, f, i, n[h + 5], 12, 1200080426)),\n (i = l(i, a, c, f, n[h + 6], 17, -1473231341)),\n (f = l(f, i, a, c, n[h + 7], 22, -45705983)),\n (c = l(c, f, i, a, n[h + 8], 7, 1770035416)),\n (a = l(a, c, f, i, n[h + 9], 12, -1958414417)),\n (i = l(i, a, c, f, n[h + 10], 17, -42063)),\n (f = l(f, i, a, c, n[h + 11], 22, -1990404162)),\n (c = l(c, f, i, a, n[h + 12], 7, 1804603682)),\n (a = l(a, c, f, i, n[h + 13], 12, -40341101)),\n (i = l(i, a, c, f, n[h + 14], 17, -1502002290)),\n (c = v(\n c,\n (f = l(f, i, a, c, n[h + 15], 22, 1236535329)),\n i,\n a,\n n[h + 1],\n 5,\n -165796510\n )),\n (a = v(a, c, f, i, n[h + 6], 9, -1069501632)),\n (i = v(i, a, c, f, n[h + 11], 14, 643717713)),\n (f = v(f, i, a, c, n[h], 20, -373897302)),\n (c = v(c, f, i, a, n[h + 5], 5, -701558691)),\n (a = v(a, c, f, i, n[h + 10], 9, 38016083)),\n (i = v(i, a, c, f, n[h + 15], 14, -660478335)),\n (f = v(f, i, a, c, n[h + 4], 20, -405537848)),\n (c = v(c, f, i, a, n[h + 9], 5, 568446438)),\n (a = v(a, c, f, i, n[h + 14], 9, -1019803690)),\n (i = v(i, a, c, f, n[h + 3], 14, -187363961)),\n (f = v(f, i, a, c, n[h + 8], 20, 1163531501)),\n (c = v(c, f, i, a, n[h + 13], 5, -1444681467)),\n (a = v(a, c, f, i, n[h + 2], 9, -51403784)),\n (i = v(i, a, c, f, n[h + 7], 14, 1735328473)),\n (c = g(\n c,\n (f = v(f, i, a, c, n[h + 12], 20, -1926607734)),\n i,\n a,\n n[h + 5],\n 4,\n -378558\n )),\n (a = g(a, c, f, i, n[h + 8], 11, -2022574463)),\n (i = g(i, a, c, f, n[h + 11], 16, 1839030562)),\n (f = g(f, i, a, c, n[h + 14], 23, -35309556)),\n (c = g(c, f, i, a, n[h + 1], 4, -1530992060)),\n (a = g(a, c, f, i, n[h + 4], 11, 1272893353)),\n (i = g(i, a, c, f, n[h + 7], 16, -155497632)),\n (f = g(f, i, a, c, n[h + 10], 23, -1094730640)),\n (c = g(c, f, i, a, n[h + 13], 4, 681279174)),\n (a = g(a, c, f, i, n[h], 11, -358537222)),\n (i = g(i, a, c, f, n[h + 3], 16, -722521979)),\n (f = g(f, i, a, c, n[h + 6], 23, 76029189)),\n (c = g(c, f, i, a, n[h + 9], 4, -640364487)),\n (a = g(a, c, f, i, n[h + 12], 11, -421815835)),\n (i = g(i, a, c, f, n[h + 15], 16, 530742520)),\n (c = m(\n c,\n (f = g(f, i, a, c, n[h + 2], 23, -995338651)),\n i,\n a,\n n[h],\n 6,\n -198630844\n )),\n (a = m(a, c, f, i, n[h + 7], 10, 1126891415)),\n (i = m(i, a, c, f, n[h + 14], 15, -1416354905)),\n (f = m(f, i, a, c, n[h + 5], 21, -57434055)),\n (c = m(c, f, i, a, n[h + 12], 6, 1700485571)),\n (a = m(a, c, f, i, n[h + 3], 10, -1894986606)),\n (i = m(i, a, c, f, n[h + 10], 15, -1051523)),\n (f = m(f, i, a, c, n[h + 1], 21, -2054922799)),\n (c = m(c, f, i, a, n[h + 8], 6, 1873313359)),\n (a = m(a, c, f, i, n[h + 15], 10, -30611744)),\n (i = m(i, a, c, f, n[h + 6], 15, -1560198380)),\n (f = m(f, i, a, c, n[h + 13], 21, 1309151649)),\n (c = m(c, f, i, a, n[h + 4], 6, -145523070)),\n (a = m(a, c, f, i, n[h + 11], 10, -1120210379)),\n (i = m(i, a, c, f, n[h + 2], 15, 718787259)),\n (f = m(f, i, a, c, n[h + 9], 21, -343485551)),\n (c = d(c, r)),\n (f = d(f, e)),\n (i = d(i, o)),\n (a = d(a, u))\n return [c, f, i, a]\n }\n\n function a(n) {\n for (var t = '', r = 32 * n.length, e = 0; e < r; e += 8)\n t += String.fromCharCode((n[e >> 5] >>> e % 32) & 255)\n return t\n }\n\n function h(n) {\n var t = []\n for (t[(n.length >> 2) - 1] = void 0, e = 0; e < t.length; e += 1)\n t[e] = 0\n for (var r = 8 * n.length, e = 0; e < r; e += 8)\n t[e >> 5] |= (255 & n.charCodeAt(e \/ 8)) << e % 32\n return t\n }\n\n function e(n) {\n for (var t, r = '0123456789abcdef', e = '', o = 0; o < n.length; o += 1)\n (t = n.charCodeAt(o)),\n (e += r.charAt((t >>> 4) & 15) + r.charAt(15 & t))\n return e\n }\n\n function r(n) {\n return unescape(encodeURIComponent(n))\n }\n\n function o(n) {\n return a(i(h((t = r(n))), 8 * t.length))\n var t\n }\n\n function u(n, t) {\n return (function (n, t) {\n var r,\n e,\n o = h(n),\n u = [],\n c = []\n for (\n u[15] = c[15] = void 0,\n 16 < o.length && (o = i(o, 8 * n.length)),\n r = 0;\n r < 16;\n r += 1\n )\n (u[r] = 909522486 ^ o[r]), (c[r] = 1549556828 ^ o[r])\n return (\n (e = i(u.concat(h(t)), 512 + 8 * t.length)), a(i(c.concat(e), 640))\n )\n })(r(n), r(t))\n }\n\n function t(n, t, r) {\n return t ? (r ? u(t, n) : e(u(t, n))) : r ? o(n) : e(o(n))\n }\n\n return t(str)\n }\n\n \/**\n * 渲染标题内容\n * @param {object} widget 组件对象\n * @param {string} icon 图标地址\n * @param {string} title 标题内容\n * @param {bool|color} color 字体的颜色(自定义背景时使用,默认系统)\n *\/\n async renderHeader(widget, icon, title, color = false) {\n let header = widget.addStack()\n header.centerAlignContent()\n try {\n const image = await this.$request.get(icon, 'IMG')\n let _icon = header.addImage(image)\n _icon.imageSize = new Size(14, 14)\n _icon.cornerRadius = 4\n } catch (e) {\n console.log(e)\n }\n header.addSpacer(10)\n let _title = header.addText(title)\n if (color) _title.textColor = color\n _title.textOpacity = 0.7\n _title.font = Font.boldSystemFont(12)\n _title.lineLimit = 1\n widget.addSpacer(15)\n return widget\n }\n\n \/**\n * @param message 描述内容\n * @param options 按钮\n * @returns {Promise<number>}\n *\/\n\n async generateAlert(message, options) {\n let alert = new Alert()\n alert.message = message\n\n for (const option of options) {\n alert.addAction(option)\n }\n return await alert.presentAlert()\n }\n\n \/**\n * 弹出一个通知\n * @param {string} title 通知标题\n * @param {string} body 通知内容\n * @param {string} url 点击后打开的URL\n *\/\n async notify(title, body, url, opts = {}) {\n let n = new Notification()\n n = Object.assign(n, opts)\n n.title = title\n n.body = body\n if (url) n.openURL = url\n return await n.schedule()\n }\n\n \/**\n * 给图片加一层半透明遮罩\n * @param {Image} img 要处理的图片\n * @param {string} color 遮罩背景颜色\n * @param {float} opacity 透明度\n *\/\n async shadowImage(img, color = '#000000', opacity = 0.7) {\n if (!img) return\n if (opacity === 0) return img\n let ctx = new DrawContext()\n \/\/ 获取图片的尺寸\n ctx.size = img.size\n\n ctx.drawImageInRect(\n img,\n new Rect(0, 0, img.size['width'], img.size['height'])\n )\n ctx.setFillColor(new Color(color, opacity))\n ctx.fillRect(new Rect(0, 0, img.size['width'], img.size['height']))\n return await ctx.getImage()\n }\n\n \/**\n * 获取当前插件的设置\n * @param {boolean} json 是否为json格式\n *\/\n getSettings(json = true) {\n let res = json ? {} : ''\n let cache = ''\n if (Keychain.contains(this.SETTING_KEY)) {\n cache = Keychain.get(this.SETTING_KEY)\n }\n if (json) {\n try {\n res = JSON.parse(cache)\n } catch (e) {}\n } else {\n res = cache\n }\n\n return res\n }\n\n \/**\n * 存储当前设置\n * @param {bool} notify 是否通知提示\n *\/\n saveSettings(notify = true) {\n let res =\n typeof this.settings === 'object'\n ? JSON.stringify(this.settings)\n : String(this.settings)\n Keychain.set(this.SETTING_KEY, res)\n if (notify) this.notify('设置成功', '桌面组件稍后将自动刷新')\n }\n\n \/**\n * 获取当前插件是否有自定义背景图片\n * @reutrn img | false\n *\/\n getBackgroundImage() {\n let result = null\n if (this.FILE_MGR_LOCAL.fileExists(this.BACKGROUND_KEY)) {\n result = Image.fromFile(this.BACKGROUND_KEY)\n }\n if (\n Device.isUsingDarkAppearance() &&\n this.FILE_MGR_LOCAL.fileExists(this.BACKGROUND_NIGHT_KEY)\n ) {\n result = Image.fromFile(this.BACKGROUND_NIGHT_KEY)\n }\n return result\n }\n\n \/**\n * 设置当前组件的背景图片\n * @param {Image} img\n *\/\n setBackgroundImage(img, notify = true) {\n if (!img) {\n \/\/ 移除背景\n if (this.FILE_MGR_LOCAL.fileExists(this.BACKGROUND_KEY)) {\n this.FILE_MGR_LOCAL.remove(this.BACKGROUND_KEY)\n }\n if (notify)\n this.notify('移除成功', '小组件白天背景图片已移除,稍后刷新生效')\n } else {\n \/\/ 设置背景\n \/\/ 全部设置一遍,\n this.FILE_MGR_LOCAL.writeImage(this.BACKGROUND_KEY, img)\n if (notify)\n this.notify('设置成功', '小组件白天背景图片已设置!稍后刷新生效')\n }\n }\n\n setBackgroundNightImage(img, notify = true) {\n if (!img) {\n \/\/ 移除背景\n if (this.FILE_MGR_LOCAL.fileExists(this.BACKGROUND_NIGHT_KEY)) {\n this.FILE_MGR_LOCAL.remove(this.BACKGROUND_NIGHT_KEY)\n }\n if (notify)\n this.notify('移除成功', '小组件夜间背景图片已移除,稍后刷新生效')\n } else {\n \/\/ 设置背景\n \/\/ 全部设置一遍,\n this.FILE_MGR_LOCAL.writeImage(this.BACKGROUND_NIGHT_KEY, img)\n if (notify)\n this.notify('设置成功', '小组件夜间背景图片已设置!稍后刷新生效')\n }\n }\n\n getRandomArrayElements(arr, count) {\n let shuffled = arr.slice(0),\n i = arr.length,\n min = i - count,\n temp,\n index\n min = min > 0 ? min : 0\n while (i-- > min) {\n index = Math.floor((i + 1) * Math.random())\n temp = shuffled[index]\n shuffled[index] = shuffled[i]\n shuffled[i] = temp\n }\n return shuffled.slice(min)\n }\n\n textFormat = {\n defaultText: { size: 14, font: 'regular', color: this.widgetColor },\n battery: { size: 10, font: 'bold', color: this.widgetColor },\n title: { size: 16, font: 'semibold', color: this.widgetColor },\n SFMono: { size: 12, font: 'SF Mono', color: this.widgetColor },\n }\n\n provideFont = (fontName, fontSize) => {\n const fontGenerator = {\n ultralight: function () {\n return Font.ultraLightSystemFont(fontSize)\n },\n light: function () {\n return Font.lightSystemFont(fontSize)\n },\n regular: function () {\n return Font.regularSystemFont(fontSize)\n },\n medium: function () {\n return Font.mediumSystemFont(fontSize)\n },\n semibold: function () {\n return Font.semiboldSystemFont(fontSize)\n },\n bold: function () {\n return Font.boldSystemFont(fontSize)\n },\n heavy: function () {\n return Font.heavySystemFont(fontSize)\n },\n black: function () {\n return Font.blackSystemFont(fontSize)\n },\n italic: function () {\n return Font.italicSystemFont(fontSize)\n },\n }\n\n const systemFont = fontGenerator[fontName]\n if (systemFont) {\n return systemFont()\n }\n return new Font(fontName, fontSize)\n }\n\n provideText = (string, container, format) => {\n const textItem = container.addText(string)\n const textFont = format.font\n const textSize = format.size\n const textColor = format.color\n\n textItem.font = this.provideFont(textFont, textSize)\n textItem.textColor = textColor\n return textItem\n }\n}\n\n\/\/ @base.end\nconst Runing = async (Widget, default_args = '', isDebug = true, extra) => {\n let M = null\n \/\/ 判断hash是否和当前设备匹配\n if (config.runsInWidget) {\n M = new Widget(args.widgetParameter || '')\n\n if (extra) {\n Object.keys(extra).forEach((key) => {\n M[key] = extra[key]\n })\n }\n const W = await M.render()\n try {\n if (M.settings.refreshAfterDate) {\n W.refreshAfterDate = new Date(\n new Date() + 1000 * 60 * parseInt(M.settings.refreshAfterDate)\n )\n }\n } catch (e) {\n console.log(e)\n }\n if (W) {\n Script.setWidget(W)\n Script.complete()\n }\n } else {\n let { act, __arg, __size } = args.queryParameters\n M = new Widget(__arg || default_args || '')\n if (extra) {\n Object.keys(extra).forEach((key) => {\n M[key] = extra[key]\n })\n }\n if (__size) M.init(__size)\n if (!act || !M['_actions']) {\n \/\/ 弹出选择菜单\n const actions = M['_actions']\n const table = new UITable()\n const onClick = async (item) => {\n M.widgetFamily = item.val\n w = await M.render()\n const fnc = item.val\n .toLowerCase()\n .replace(\/( |^)[a-z]\/g, (L) => L.toUpperCase())\n if (w) {\n return w[`present${fnc}`]()\n }\n }\n const preview = [\n {\n url: 'https:\/\/z3.ax1x.com\/2021\/03\/26\/6v5wIP.png',\n title: '小尺寸',\n val: 'small',\n dismissOnSelect: true,\n onClick,\n },\n {\n url: 'https:\/\/z3.ax1x.com\/2021\/03\/26\/6v5dat.png',\n title: '中尺寸',\n val: 'medium',\n dismissOnSelect: true,\n onClick,\n },\n {\n url: 'https:\/\/z3.ax1x.com\/2021\/03\/26\/6v5BPf.png',\n title: '大尺寸',\n val: 'large',\n dismissOnSelect: true,\n onClick,\n },\n ]\n await M.preferences(table, preview, '预览组件')\n const extra = []\n for (let _ in actions) {\n const iconItem = M._actionsIcon[_]\n const isUrl = typeof iconItem === 'string'\n const actionItem = {\n title: _,\n onClick: actions[_],\n }\n if (isUrl) {\n actionItem.url = iconItem\n } else {\n actionItem.icon = iconItem\n }\n extra.push(actionItem)\n }\n await M.preferences(table, extra, '配置组件')\n return table.present()\n }\n }\n}\n\n\/\/ await new DmYY().setWidgetConfig();\nmodule.exports = { DmYY, Runing }\n\n\/\/version:",
"share_sheet_inputs" : [
]
}