/** * æ ¹æ®èªå·±çä¹ æ¯æ´åå个å¼åè èå½¢æçå·¥å ·å * å¹¶ä¸å å ¥ä¸äºå¥½ç¨çæ¹æ³ * æ¹æ³å¦ä¸ï¼ * isEmptyï¼ å¤æå符串æ¯å¦æ¯ç©ºï¼undefinedï¼nullï¼ç©ºä¸²ï¼ * randomStringï¼ çæéæºå符串 * autoCompleteï¼ èªå¨è¡¥é½å符串 * customReplaceï¼ èªå®ä¹æ¿æ¢ * formatDateï¼ æ¥ææ ¼å¼å * formatTimeDuringï¼ æ¯«ç§æ°è½¬æ¢æn天må°æ¶ * fileLengthFormatï¼ æä»¶å¤§å°åä½è½¬æ¢ * * åºäºScriptableçapiå°è£ çæ¹æ³ï¼ç¨æ³å¯ä»¥åè该ç®å½ä¸/exampleä¸çdemoï¼ï¼ * require({scriptName, url = '', reload = false})ï¼ å¼å ¥ç¬¬ä¸æ¹jsåº * generateInputAlertï¼ çæå¸¦ææ¬æ¡çå¼¹çª * generateAlertï¼ çæå¼¹çª * widgetCutBgï¼ è®¾ç½®widgetèæ¯ * * â è¯·å¨æ§è¡å¤±è´¥çå°æ¹æ§è¡execFail() * * @param scriptName èæ¬åï¼ç¨äºéç¥æ¶åçæ é¢ * @param scriptId æ¯ä¸ªèæ¬å¯ä¸çidï¼ç¨äºåå¨æä¹ åçæ¶åå å ¥key * @param options ä¼ å ¥ä¸äºåæ°ï¼ç®ååæ°å¦ä¸ï¼ * lkIsSaveLog{scriptId} boolean : ä¿åæ¥å¿å°iCloudï¼ç®å½ï¼scriptable/lklogs/{scriptId}/ï¼ * lkIsEnableLog{scriptId} boolean * @constructor */ function ScriptableToolKit(scriptName, scriptId, options) { return new (class { constructor(scriptName, scriptId, options) { //scriptableå ¬å ±ç»ä»¶ this.local = FileManager.local() this.icloud = FileManager.iCloud() this.curDateCache = this.local.joinPath(this.local.documentsDirectory(), "curDateCache") //ä¸äºæ©å±åæ° this.options = options this.tgEscapeCharMapping = {'&': 'ï¼'} this.userAgent = `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.2 Safari/605.1.15` this.prefix = `lk` this.name = scriptName this.id = scriptId this.data = null this.dataFile = `${this.prefix}${this.id}.json` this.bgImgPath = `${this.prefix}${this.id}Bg.jpg` this.bgImgPath = this.local.joinPath(this.local.documentsDirectory(), this.bgImgPath) //i18n this.lang = Device.language() this.msg = { "zh": { s0:"å¨å¼å§ä¹åï¼å è¿å ¥ä¸»å±å¹ï¼è¿å ¥å¾æ æå模å¼ãæ»å°æå³è¾¹ç空ç½é¡µï¼å¹¶è¿è¡æªå¾ã", s1:"çèµ·æ¥ä½ éæ©çå¾ç䏿¯iPhoneçæªå¾ï¼æè ä½ çiPhone䏿¯æã请æ¢ä¸å¼ å¾çåè¯ä¸æ¬¡ã", s2:"ä½ æ³å建ä»ä¹å°ºå¯¸çwidgetï¼", s3:"ä½ æ³æwidgetæ¾å¨åªéï¼", s4:" (请注æï¼æ¨ç设å¤åªæ¯æä¸¤è¡å°é¨ä»¶ï¼æä»¥ä¸é´ååºé¨çé项æ¯ä¸æ ·ç)ã", s5:"widgetçèæ¯å¾å·²è£åå®æï¼æ³å¨Scriptableå é¨ä½¿ç¨è¿æ¯å¯¼åºå°ç¸åï¼", s6:"å·²ç»æªå¾ï¼ç»§ç»", s7:"éåºå»æªå¾", s8:"å°", s9:"ä¸", s10:"大", s11:"é¡¶é¨å·¦è¾¹", s12:"é¡¶é¨å³è¾¹", s13:"ä¸é´å·¦è¾¹", s14:"ä¸é´å³è¾¹", s15:"åºé¨å·¦è¾¹", s16:"åºé¨å³è¾¹", s17:"é¡¶é¨", s18:"ä¸é´", s19:"åºé¨", s20:"å¨Scriptableå é¨ä½¿ç¨", s21:"导åºå°ç¸å", s22:"å¡«åé®ç½©å±é¢è²ãï¼æ ¼å¼ï¼#000000ï¼", s23:"é¢è²ï¼æ ¼å¼ï¼#000000ï¼", s24:"å¡«åé®ç½©å±ä¸éæåº¦ï¼0-1ä¹é´ï¼", s25:"0-1ä¹é´", s26:"ç¡®å®", s27:"åæ¶", s28:"é¢è§widget", s29:"设置widgetèæ¯", s30:"å ¥å£", s31:"ä½ ç¨çæ¯åªä¸ªåå·ï¼", s32:"éåº", s33:"æ¸ é¤ç¼å", s34:"å¼å§æ¸ é¤ç¼å", s35:"æ¸ é¤ç¼å宿" }, "en": { s0:"Before you start, go to your home screen and enter wiggle mode. Scroll to the empty page on the far right and take a screenshot.", s1:"It looks like you selected an image that isn't an iPhone screenshot, or your iPhone is not supported. Try again with a different image.", s2:"What size of widget are you creating?", s3:"What position will it be in?", s4:" (Note that your device only supports two rows of widgets, so the middle and bottom options are the same.)", s5:"Your widget background is ready. Would you like to use it in a Scriptable widget or export the image?", s6:"Continue", s7:"Exit to Take Screenshot", s8:"Small", s9:"Medium", s10:"Large", s11:"Top left", s12:"Top right", s13:"Middle left", s14:"Middle right", s15:"Bottom left", s16:"Bottom right", s17:"Top", s18:"Middle", s19:"Bottom", s20:"Use in Scriptable", s21:"Export to Photos", s22:"Fill in the mask layer color. (Format: #000000)", s23:"Color.(Format: #000000)", s24:"Fill in the mask layer opacity (between 0-1)", s25:"between 0-1", s26:"Confirm", s27:"Cancel", s28:"Preview widget", s29:"Setting widget background", s30:"ENTER", s31:"What type of iPhone do you have?", s32:"Exit", s33:"Clean cache", s34:"Clean cache started", s35:"Clean cache finished" } } this.curLang = this.msg[this.lang] || this.msg.en //é»è®¤èæ¬å¼å ³ this.isSaveLog = this.getResultByKey(`${this.prefix}IsSaveLog${this.id}`, false) this.isEnableLog = this.getResultByKey(`${this.prefix}IsEnableLog${this.id}`, true) this.logDir = this.icloud.documentsDirectory() + '/lklogs/' + this.id this.logSeparator = '\nââ' this.now = new Date() this.execStatus = true this.notifyInfo = [] this.operations = [] //èæ¬æ§è¡éå¶ this.isLimited = false this.checkLimit() } async checkLimit() { const lastRunningTime = await this.getVal(`${this.prefix}LastRunningTime${this.id}`, 'local', 0) const runLimitNum = this.getResultByKey(`${this.prefix}RunLimitNum${this.id}`, 300000) this.log(`䏿¬¡è¿è¡æ¶é´ï¼${this.formatDate(new Date(lastRunningTime), "yyyy-MM-dd HH:mm:ss.S")} ${lastRunningTime}ï¼è¿è¡é¢çéå¶ï¼${runLimitNum}`) if (lastRunningTime >= 0) { if (this.now.getTime() - lastRunningTime <= runLimitNum) { this.appendNotifyInfo('éå¶è¿è¡') this.isLimited = true } else { await this.setVal(`${this.prefix}LastRunningTime${this.id}`, this.now.getTime(), 'local') } } return this.isLimited } getResultByKey(key, defaultValue) { if (!this.options) { return defaultValue } const val = this.options[key] if (this.isEmpty(val)) { return defaultValue } else { return val } } appendNotifyInfo(info, type) { if (type == 1) { this.notifyInfo = info } else { this.notifyInfo.push(`${this.logSeparator}${this.formatDate(new Date(), 'yyyy-MM-dd HH:mm:ss.S')}â${info}`) } } saveLog() { if (this.isSaveLog) { let message if (Array.isArray(this.notifyInfo)) { message = this.notifyInfo.join("") } else { message = this.notifyInfo } // æ ¡éªlklogç®å½æ¯å¦åå¨ if (!this.icloud.isDirectory(this.logDir)) { // create dir this.icloud.createDirectory(this.logDir, true) } // write log this.icloud.writeString(`${this.logDir}/${this.formatDate(this.now, 'yyyyMMddHHmmss')}.log`, message) } } prependNotifyInfo(info) { this.notifyInfo.splice(0, 0, info) } execFail() { this.execStatus = false } sleep(time) { return new Promise((resolve) => setTimeout(resolve, time)) } log(message) { if (this.isEnableLog) console.log(`${this.logSeparator}${JSON.stringify(message)}`) this.appendNotifyInfo(message) } logErr(message) { this.execStatus = false if (this.isEnableLog) { console.warn(`${this.logSeparator}${this.name}æ§è¡å¼å¸¸:`) console.warn(message) console.warn(`\n${message.message}`) } } getContainer(key) { return key == 'local' ? this.local : this.icloud } /** * get value from container * @param key * @param container local or icloud */ async getVal(key, container, defaultValue) { let containerInstance = this.getContainer(container) let data = '' try { let realDataFile = containerInstance.joinPath(containerInstance.documentsDirectory(), this.dataFile) if (!containerInstance.fileExists(realDataFile)) { await this.setVal(key, defaultValue, container) return defaultValue } data = await containerInstance.readString(realDataFile) data = JSON.parse(data) } catch (e) { throw e } if (data.hasOwnProperty(key)) { return data[key] } else { await this.setVal(key, defaultValue, container) return defaultValue } } /** * get dataFile content * @param container */ async getDataFile(container) { let containerInstance = this.getContainer(container) let data = '' try { let realDataFile = containerInstance.joinPath(containerInstance.documentsDirectory(), this.dataFile) if (!containerInstance.fileExists(realDataFile)) { return Promise.resolve('') } data = await containerInstance.readString(realDataFile) } catch (e) { throw e } return Promise.resolve(data) } async saveImage(fileName, image, container) { let containerInstance = this.getContainer(container) let imagePath = containerInstance.joinPath(containerInstance.documentsDirectory(), `${this.prefix}${this.id}/${fileName}`) let imageDir = imagePath.substring(0, imagePath.lastIndexOf("/") + 1) if (!containerInstance.isDirectory(imageDir)) { containerInstance.createDirectory(imageDir, true) } containerInstance.writeImage(imagePath, image) } async getImage(fileName, container) { let containerInstance = this.getContainer(container) let imagePath = containerInstance.joinPath(containerInstance.documentsDirectory(), `${this.prefix}${this.id}/${fileName}`) if (!containerInstance.fileExists(imagePath)) { this.logErr(`file not exist: ${imagePath}`) return false } return await containerInstance.readImage(imagePath) } /** * set value in container * @param key * @param val * @param container this.local or this.icloud */ async setVal(key, val, container) { let containerInstance = this.getContainer(container) let data let realDataFile = containerInstance.joinPath(containerInstance.documentsDirectory(), this.dataFile) try { if (!containerInstance.fileExists(realDataFile)) { data = {} } else { data = await containerInstance.readString(realDataFile) data = JSON.parse(data) } } catch (e) { data = {} } data[key] = val await containerInstance.writeString(realDataFile, JSON.stringify(data)) } async get(options, callback = () => {}) { let request = new Request('') request.url = options.url request.method = 'GET' request.headers = options.headers try { const result = await request.loadString() callback(request.response, result) return result } catch (e) { this.logErr(e) callback(undefined, undefined) } } async post(options, callback = () => {}) { let request = new Request('') request.url = options.url request.body = options.body request.method = 'POST' request.headers = options.headers request.timeout = 5000 try { const result = await request.loadString() callback(request.response, result) return result } catch (e) { this.logErr(e) callback(undefined, undefined) } } async loadScript ({scriptName, url}) { this.log(`è·åèæ¬ã${scriptName}ã`) const content = await this.get({url}) this.icloud.writeString(`${this.icloud.documentsDirectory()}/${scriptName}.js`, content) this.log(`è·åèæ¬ã${scriptName}ã宿ð`) } require({scriptName, url = '', reload = false}) { if (this.icloud.fileExists(this.icloud.joinPath(this.icloud.documentsDirectory(), `${scriptName}.js`)) && !reload) { this.log(`å¼ç¨èæ¬ã${scriptName}ã`) return importModule(scriptName) } else { this.loadScript({ scriptName, url }) this.log(`å¼ç¨èæ¬ã${scriptName}ã`) return importModule(scriptName) } } async generateInputAlert(message, field, defaultValue) { let result = [] let alert = new Alert() alert.message = message alert.addTextField(field, defaultValue); alert.addCancelAction(this.curLang.s27) alert.addAction(this.curLang.s26) result[0] = await alert.presentAlert() result[1] = alert.textFieldValue(0) return result } async generateAlert(message, options) { let alert = new Alert() alert.message = message for (const option of options) { alert.addAction(option) } return await alert.presentAlert() } isEmpty(obj) { return typeof obj == "undefined" || obj == null || obj == "" || obj == "null" } isWorkingDays(now, workingDaysFlag = 'curlybraces', holidayFlag = 'gamecontroller') { return new Promise(async (resolve, reject) => { let sp = "â" const d = this.formatDate(now, 'yyyy-MM-dd') // 0工使¥ 1伿¯æ¥ 2è忥 3è¡¥ç let resultStr = 0 try { let curDate = await this.getVal('curDateCache', 'local', 'fff') //夿ä¸ä¸æ¬¡æ¯å¦è¯·æ±é误 let curDateErrorTime = await this.getVal('curDateCacheErrorTime', 'local', this.now.getTime()) let isPreError = !this.isEmpty(curDateErrorTime) && Number(curDateErrorTime) + (5 * 60 * 1000) < this.now.getTime() if (!isPreError && d == curDate.split(sp)[0] && curDate.split(sp)[1] != "â") { //æ¥æç¸å说æå½å¤©è¯·æ±è¿ï¼ç´æ¥ä½¿ç¨ä¸æ¬¡è¯·æ±çå¼ resultStr = curDate.split(sp)[1] this.log('already request') // this.setVal('curDateCache', '', 'local') } else { this.log('send request') const url = { url: 'http://timor.tech/api/holiday/info/' + d } await this.get(url, (resp, data) => { if (data.indexOf("<") == 0) { resultStr = "â" } else { resultStr = JSON.parse(data) if (resultStr.code == -1) { // æ¥å£é误 resultStr = "â" } else { resultStr = resultStr.type.type } } }) } } catch (e) { resultStr = "â" this.logErr(e) } finally { // åå ¥æä»¶ç³»ç» await this.setVal('curDateCache', `${d}${sp}${resultStr}`, 'local') if (resultStr == "â") { resolve(resultStr) // åå ¥é误æ¶é´ï¼ä¾¿äº5åéåéæ°è¯·æ± this.log('åå ¥è¿è¡é误æ¶é´ï¼5åéåéæ°è¯·æ±ï¼') this.setVal('curDateCache', '', 'local') this.setVal('curDateCacheErrorTime', `${this.now.getTime()}`, 'local') } else { this.setVal('curDateCacheErrorTime', '', 'local') this.setVal('curDateCache', `${d}${sp}${resultStr}`, 'local') resolve(resultStr == 0 || resultStr == 3 ? workingDaysFlag : holidayFlag) } } }) } randomString(len) { len = len || 32 var $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890' var maxPos = $chars.length var pwd = '' for (let i = 0; i < len; i++) { pwd += $chars.charAt(Math.floor(Math.random() * maxPos)) } return pwd } /** * * 示ä¾:$.time('yyyy-MM-dd qq HH:mm:ss.S') * :$.time('yyyyMMddHHmmssS') * y:å¹´ M:æ d:æ¥ q:å£ H:æ¶ m:å s:ç§ S:æ¯«ç§ * å ¶ä¸yå¯é0-4ä½å ä½ç¬¦ãSå¯é0-1ä½å ä½ç¬¦ï¼å ¶ä½å¯é0-2ä½å ä½ç¬¦ * @param {*} format æ ¼å¼ååæ° * */ formatDate(date, format) { let o = { 'M+': date.getMonth() + 1, 'd+': date.getDate(), 'H+': date.getHours(), 'm+': date.getMinutes(), 's+': date.getSeconds(), 'q+': Math.floor((date.getMonth() + 3) / 3), 'S': date.getMilliseconds() } if (/(y+)/.test(format)) format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)) for (let k in o) if (new RegExp('(' + k + ')').test(format)) format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length)) return format } /** * èªå¨è¡¥é½å符串 * @param str åå§å符串 * @param prefix åç¼ * @param suffix åç¼ * @param fill è¡¥é½ç¨å符 * @param len ç®æ è¡¥é½é¿åº¦ï¼ä¸å å«ååç¼ * @param direction æ¹åï¼0å¾åè¡¥é½ * @param ifCode æ¯å¦æç * @param clen æç é¿åº¦ * @param startIndex èµ·å§åæ * @param cstr æç å符 * @returns {*} */ autoComplete(str, prefix, suffix, fill, len, direction, ifCode, clen, startIndex, cstr) { str += `` if (str.length < len) { while (str.length < len) { if (direction == 0) { str += fill } else { str = fill + str } } } if (ifCode) { let temp = `` for (var i = 0; i < clen; i++) { temp += cstr } str = str.substring(0, startIndex) + temp + str.substring(clen + startIndex) } str = prefix + str + suffix return this.toDBC(str) } /** * @param str æºå符串 "#{code}, #{value}" * @param param ç¨äºæ¿æ¢çæ°æ®ï¼ç»æå¦ä¸ * @param prefix åç¼ "#{" * @param suffix åç¼ "}" * { * "code": 1, * "value": 2 * } * æä¸é¢çä¼ å ¥ï¼è¾åºä¸º"1, 2" * 对åºç#{code}ç¨paraméé¢codeç弿¿æ¢ï¼#{value}乿¯ * @returns {*|void|string} */ customReplace(str, param, prefix, suffix) { try { if (this.isEmpty(prefix)) { prefix = "#{" } if (this.isEmpty(suffix)) { suffix = "}" } for (let i in param) { str = str.replace(`${prefix}${i}${suffix}`, param[i]) } } catch (e) { this.logErr(e) } return str } toDBC(txtstring) { var tmp = "" for (var i = 0; i < txtstring.length; i++) { if (txtstring.charCodeAt(i) == 32) { tmp = tmp + String.fromCharCode(12288) } else if (txtstring.charCodeAt(i) < 127) { tmp = tmp + String.fromCharCode(txtstring.charCodeAt(i) + 65248) } } return tmp } getWidgetBg() { return this.local.readImage(this.bgImgPath) } phoneSizes() { return { // 16 Pro Max "2868": { small: 510, medium: 1092, large: 1146, left: 114, right: 696, top: 276, middle: 912, bottom: 1548, }, // 14 Pro Max "2796": { small: 510, medium: 1092, large: 1146, left: 99, right: 681, top: 282, middle: 918, bottom: 1554 }, // 14 Pro "2556": { small: 474, medium: 1014, large: 1062, left: 82, right: 622, top: 270, middle: 858, bottom: 1446 }, // 12 Pro Max "2778": { small: 510, medium: 1092, large: 1146, left: 96, right: 678, top: 246, middle: 882, bottom: 1518 }, // 12 and 12 Pro "2532": { small: 474, medium: 1014, large: 1062, left: 78, right: 618, top: 231, middle: 819, bottom: 1407 }, // 11 Pro Max, XS Max "2688": { small: 507, medium: 1080, large: 1137, left: 81, right: 654, top: 228, middle: 858, bottom: 1488 }, // 11, XR "1792": { small: 338, medium: 720, large: 758, left: 54, right: 436, top: 160, middle: 580, bottom: 1000 }, // 11 Pro, XS, X, 12 mini "2436": { x: { small: 465, medium: 987, large: 1035, left: 69, right: 591, top: 213, middle: 783, bottom: 1353, }, mini: { small: 465, medium: 987, large: 1035, left: 69, right: 591, top: 231, middle: 801, bottom: 1371, } }, // Plus phones "2208": { small: 471, medium: 1044, large: 1071, left: 99, right: 672, top: 114, middle: 696, bottom: 1278 }, // SE2 and 6/6S/7/8 "1334": { small: 296, medium: 642, large: 648, left: 54, right: 400, top: 60, middle: 412, bottom: 764 }, // SE1 "1136": { small: 282, medium: 584, large: 622, left: 30, right: 332, top: 59, middle: 399, bottom: 399 }, // 11 and XR in Display Zoom mode "1624": { small: 310, medium: 658, large: 690, left: 46, right: 394, top: 142, middle: 522, bottom: 902 }, // Plus in Display Zoom mode "2001": { small: 444, medium: 963, large: 972, left: 81, right: 600, top: 90, middle: 618, bottom: 1146 } } } remove(path) { this.local.remove(path) } cropImage(img, rect, color, opacity) { let draw = new DrawContext() draw.size = new Size(rect.width, rect.height) draw.drawImageAtPoint(img, new Point(-rect.x, -rect.y)) draw.setFillColor(new Color(color, Number(opacity))) draw.fillRect(new Rect(0, 0, img.size["width"], img.size["height"])) return draw.getImage() } async widgetCutBg() { // Determine if user has taken the screenshot. var message message = this.curLang.s0 let exitOptions = [this.curLang.s6, this.curLang.s7] let shouldExit = await this.generateAlert(message, exitOptions) if (shouldExit) return // Get screenshot and determine phone size. let img = await Photos.fromLibrary() let height = img.size.height let phone = this.phoneSizes()[height] if (!phone) { message = this.curLang.s1 await this.generateAlert(message, ["OK"]) return } // Extra setup needed for 2436-sized phones. if (height == 2436) { message = this.curLang.s31 let types = ["iPhone 12 mini", "iPhone 11 Pro, XS, X"] let typeIndex = await this.generateAlert(message, types) let type = (typeIndex == 0) ? "mini" : "x" phone = phone[type] } // Prompt for widget size and position. message = this.curLang.s2 let sizes = [this.curLang.s8, this.curLang.s9, this.curLang.s10] let size = await this.generateAlert(message, sizes) message = this.curLang.s3 message += (height == 1136 ? this.curLang.s4 : "") // Determine image crop based on phone size. let crop = {w: "", h: "", x: "", y: ""} if (size == 0) { crop.w = phone.small crop.h = phone.small let positions = ["Top left", "Top right", "Middle left", "Middle right", "Bottom left", "Bottom right"] let positionsString = [this.curLang.s11, this.curLang.s12, this.curLang.s13, this.curLang.s14, this.curLang.s15, this.curLang.s16] let position = await this.generateAlert(message, positionsString) // Convert the two words into two keys for the phone size dictionary. let keys = positions[position].toLowerCase().split(' ') crop.y = phone[keys[0]] crop.x = phone[keys[1]] } else if (size == 1) { crop.w = phone.medium crop.h = phone.small // Medium and large widgets have a fixed x-value. crop.x = phone.left let positions = ["Top", "Middle", "Bottom"] let positionsString = [this.curLang.s17, this.curLang.s18, this.curLang.s19] let position = await this.generateAlert(message, positionsString) let key = positions[position].toLowerCase() crop.y = phone[key] } else if (size == 2) { crop.w = phone.medium crop.h = phone.large crop.x = phone.left let positionsString = [this.curLang.s17, this.curLang.s19] let position = await this.generateAlert(message, positionsString) // Large widgets at the bottom have the "middle" y-value. crop.y = position ? phone.middle : phone.top } // set mask layer color let maskLayerColor = await this.generateInputAlert(this.curLang.s22, this.curLang.s23, '#000000') if(maskLayerColor[0] == -1) return let opacity = await this.generateInputAlert(this.curLang.s24, this.curLang.s25, '0.1') if(opacity[0] == -1) return // Crop image and finalize the widget. let imgCrop = this.cropImage(img, new Rect(crop.x, crop.y, crop.w, crop.h), maskLayerColor[1], opacity[1]) message = this.curLang.s5 const exportPhotoOptions = [this.curLang.s20, this.curLang.s21] const exportPhoto = await this.generateAlert(message, exportPhotoOptions) if (exportPhoto) { Photos.save(imgCrop) } else { this.local.writeImage(this.bgImgPath, imgCrop) } Script.complete() } /** * èªå®ä¹æä½å ¥å£ * @param customEnter [{name:"æä½1", callback: function(){}}] * @param isReset trueï¼æ¸ 空èªå¸¦çæä½ * falseï¼å¨èªå¸¦çæä½åé¢è¡¥å */ async widgetEnter(customEnter, isReset) { // æ¸ ç©ºä¸æ¬¡è¿è¡æ¶é´ï¼é²æ¢ç¬¬ä¸æ¬¡è¿è¡å°ç»ä»¶ä¸æåå¯¼è´æ æ³ç»§ç»è°æ´ await this.setVal('lastRunningTime', 0, 'local') let options = [this.curLang.s28, this.curLang.s29, this.curLang.s33] if (Array.isArray(customEnter)) { let customEnterNames = customEnter.map((item, index) => { return item.name[this.lang] }) let customEnterCallback = customEnter.map((item, index) => { return item.callback }) if (isReset) { options = customEnterNames } else { this.operations.push({callback: main}) this.operations.push({callback: function(){$.widgetCutBg()}}) this.operations.push({callback: function(){$.cleanCache()}}) options = options.concat(customEnterNames) } customEnterCallback.forEach((callback)=>{this.operations.push({callback: callback})}) } options.push(this.curLang.s32) this.operations.push({callback: function(){}}) return await this.generateAlert(this.curLang.s30, options) } async handleOperations(index) { await this.operations[index].callback() } cleanCache() { this.log(this.curLang.s34) let filePath = this.local.joinPath(this.local.documentsDirectory(), this.dataFile) if (this.local.fileExists(filePath)) { this.local.remove(filePath) } filePath = this.bgImgPath if (this.local.fileExists(filePath)) { this.local.remove(filePath) } this.log(this.curLang.s35) } formatTimeDuring(total, lang = "zh", n = 0) { total = Number(total); let zhUnitArr = ["毫ç§", "ç§", "åé", "å°æ¶", "天", "æ", "å¹´"]; let enUnitArr = ["ms", "s", "min", "h", "d", "m", "y"]; let scaleArr = [1000.0, 60.0, 60.0, 24.0, 30.0, 12.0, 100]; let len = total; if (len > scaleArr[n]) { len = total / scaleArr[n]; return this.formatTimeDuring(len, lang, ++n); } else { let unit = zhUnitArr[n] if (lang === "en") { unit = enUnitArr[n] } return len.toFixed(2) + "" + unit; } } fileLengthFormat(total, unit = "", toByte = false) { total = Number(total); var unitArr = ["", "KB", "MB", "GB", "TB", "PB", "EB", "ZB"]; var n = 0; try { n = unitArr.indexOf(unit); } catch (e) { throw e; } if (toByte) { if (n == 0) { return total; } return this.fileLengthFormat(total * 1024, unitArr[--n], true); } var len = total; if (len > 1000) { len = total / 1024.0; return this.fileLengthFormat(len, unitArr[++n]); } else { if (n == 0) { return len.toFixed(2); } return len.toFixed(2) + " " + unitArr[n]; } } })(scriptName, scriptId, options) }