!function () { // 暗黑模式 if (utools.isDarkColors()) { !$('#darkmode').length && $('head').append(` `) } else { $('#darkmode').length && $('#darkmode, #darkswal').remove() } // 禁用危险函数 let utoolsFull = utools if (!isDev()) utools = getuToolsLite() // 数据库前缀 const QC_PREFIX = 'qc_' const CFG_PREFIX = 'cfg_' // 数据库函数封装 let getDB = id => { let db = utoolsFull.db.get(id) return db ? db.data : {} } let putDB = (value, id) => { let db = utoolsFull.db.get(id); if (db) utoolsFull.db.put({ _id: id, data: value, _rev: db._rev }) else utoolsFull.db.put({ _id: id, data: value }); } let delDB = id => { return utoolsFull.db.remove(id) } let getDocs = key => { return utoolsFull.db.allDocs(key) } // 获取所有 qc,等效于 1.6 版本 getDB('customFts') let getAllQuickCommands = () => { let allQcs = {} getDocs(QC_PREFIX).forEach(x => allQcs[x.data.features.code] = x.data) return allQcs } // 进入插件 utools.onPluginEnter(async ({ code, type, payload }) => { if (isRunningAtFirstTime()) { showChangeLog() importDefaultCommands() oldVersionFix() } var handleEnter utools.onPluginOut(() => { // 暂存 codeRunner 的内容 if (code == "code") { var cmd = window.editor.getValue(); var program = $('#program').val(), scptarg = $('#scptarg').val(), customoptions; if (program == 'custom') customoptions = { custombin: $('#custombin').val(), customarg: $('#customarg').val(), customext: $('#customext').val(), customcodec: $('#customcodec').val() } putDB({ cmd: cmd, program: program, scptarg: scptarg, customoptions: customoptions }, CFG_PREFIX + 'codeHistory') } // 初始化 $("#options, #out").empty() $('body').children(':not(#wrapper)').remove() $('body').css({overflow: 'hidden'}) if (handleEnter) document.removeEventListener('keydown', handleEnter) }) // 配置页面 if (code == 'options') { utools.setExpendHeight(600); // $("#options").show(); showOptions(); } else if (code == 'code') { var file = "" // utools.setExpendHeight(600); if (type == 'files') file = payload[0].path showCodeEditor(file) } else { // console.log(new Date().getTime() - window.startTime); $('body').css({overflow: 'auto'}) utools.setExpendHeight(0); $("#options").hide(); var db = getDB(QC_PREFIX + code), cmd = db.cmd; if (db.program == "custom") { option = db.customOptions; } else if(db.program == "quickcommand"){ option = { mode: "quickcommand", enterData: { code, type, payload } }; }else{ option = programs[db.program]; } option.scptarg = db.scptarg cmd = special(cmd); // 正则 if (type == 'regex') cmd = cmd.replace(/\{\{input\}\}/mg, payload); // 文件 if (type == 'files' && cmd.includes('{{MatchedFiles')) { let MatchedFiles = payload let Matched = cmd.match(/\{\{MatchedFiles(\[\d+\]){0,1}(\.\w{1,11}){0,1}\}\}/g) Matched && Matched.forEach(m => { repl = eval(m.slice(2, -2)) typeof repl == 'object' ? (repl = JSON.stringify(repl)) : (repl = repl.replace('\\', '\\\\')) cmd = cmd.replace(m, repl.replace('$','$$$')) }) } // 窗口 var repl if (type == 'window') { // 获取选中的文件 if (cmd.includes('{{SelectFile}}')) { repl = await getSelectFile(payload.id); cmd = cmd.replace(/\{\{SelectFile\}\}/mg, repl) } // 获取资源管理器或访达当前目录 if (cmd.includes('{{pwd}}')) { repl = getCurrentFolderPathFix(); console.log(repl); cmd = cmd.replace(/\{\{pwd\}\}/mg, repl) console.log(cmd) } // 获取窗口信息 if (cmd.includes('{{WindowInfo')) { let WindowInfo = payload let Matched = cmd.match(/\{\{WindowInfo(\.\w{1,7}){0,1}\}\}/g) Matched && Matched.forEach(m => { repl = eval(m.slice(2, -2)) typeof repl == 'object' && (repl = JSON.stringify(repl)) cmd = cmd.replace(m, repl) }) } } // 无输出的批处理 // if (db.output == 'ignore' && option.ext == 'bat') option.bin = 'explorer'; if (db.hasSubInput) { // 启动子命令输入 // 清空输出 // $("#out").empty(); var rule = String.raw`\{\{subinput(:.+?){0,1}\}\}` var matched = cmd.match(new RegExp(rule)) var placeholder = matched[1] || ':请输入' var subinput = ''; var setSubInput = () => { utools.setSubInput(({text}) => { subinput = text; }, placeholder.slice(1)); } handleEnter = (event) => { if (event.keyCode == 13) { $("#out").append(`

>> ${new Date()}

`); var cmdToRun = cmd.replace(new RegExp(rule, 'g'), subinput); runQuickCommand(cmdToRun, option, db.output, true); } }; setSubInput(); document.addEventListener('keydown', handleEnter); } else { runQuickCommand(cmd, option, db.output, false); } } }); let runQuickCommand = (cmd, option, output, autoScroll = false, autoHeight = true) => { // 不需要输出的,提前关闭窗口 if (['ignore', 'clip', 'send', 'notice', 'terminal'].indexOf(output) !== -1) { utools.hideMainWindow(); setTimeout(() => { utools.outPlugin(); }, 500); } var outputOpts = { type: output, autoScroll: autoScroll, autoHeight: autoHeight } if (option.mode) { // 内置环境 runCodeInVm(cmd, (stdout, stderr) => { if (cmd.includes("utools.setExpendHeight")) outputOpts.autoHeight = false switchQuickCommandResult(stdout, stderr, outputOpts) }, option.enterData) } else { var terminal = output == 'terminal' ? true : false outputOpts.scriptPath = getQuickCommandScriptFile(option.ext) // 执行脚本 runCodeFile(cmd, option, terminal, (stdout, stderr) => { switchQuickCommandResult(stdout, stderr, outputOpts) }) } } let switchQuickCommandResult = (stdout, stderr, outputOpts) => { var output = outputOpts.type, autoScroll = outputOpts.autoScroll, autoHeight = outputOpts.autoHeight; var outputAutoFix = (autoScroll, autoHeight) => { var outputHeight = $("#out").outerHeight() if (outputHeight > 600) outputHeight = 600 if (autoHeight && $('#options').is(':hidden')) utools.setExpendHeight(outputHeight); if (outputHeight == 600 && autoScroll) $(document).scrollTop($(document).height()); } if (stderr) { $("#out").addClass('error') // 报错 if (output == 'text' || output == 'html') { $("#out").append(stderr) outputAutoFix(autoScroll, autoHeight) } else { var index = utools.showMessageBox({ type: 'error', title: '啊嘞?!', message: stderr, buttons: outputOpts.scriptPath ? ['转至脚本目录', '退出'] : ['退出'] }) if (outputOpts.scriptPath && index == 0) { locate(outputOpts.scriptPath ); } copyTo(stderr); message("已复制报错信息"); utools.outPlugin(); } } else if (stdout) { $("#out").removeClass("error") // 有输出 switch (output) { case "text": $("#out").append(htmlEncode(stdout, true)) outputAutoFix(autoScroll, autoHeight) break; case "html": $("#out").append(stdout) outputAutoFix(autoScroll, autoHeight) break; case "clip": copyTo(stdout) break; case "send": send(stdout) break; case "notice": // 发送系统通知 message(stdout) break; case "ignore": case "nothing": break; default: break; } // } else { // // 无输出 // utools.outPlugin() } } // 替换上个版本弃用的功能 let oldVersionFix = () => { var customFts = getDB('customFts'); let ftsKeys = Object.keys(customFts); if (!ftsKeys.length) return; utools.showNotification('正在对老版本命令做兼容处理,如插件显示空白请稍候', 'warning') ftsKeys.forEach((x, i) => { let fts = customFts[x] // 旧版的 program if (fts.program == 'simulation') fts.program = 'quickcommand'; // 旧版的 sleep if (fts.cmd.includes('await sleep')) fts.cmd = fts.cmd.replace(/await sleep/g, 'quickcommand.sleep') // 旧版的 match.app let type = fts.features.cmds[0].type || 'key' if (type == 'window') { let windowMatch = fts.features.cmds[0].match console.log(windowMatch) if (windowMatch && (typeof windowMatch.app == 'string')) { console.log(fts); fts.features.cmds[0].match.app = windowMatch.app.split(',') } } // 不规范的 code let code = fts.features.code if (!/^(window|key|regex|files|default)_/.test(code)) { console.log(code); utoolsFull.removeFeature(code) let uid = Number(Math.random().toString().substr(3, 3) + (Date.now() + i * 10000)).toString(36) code = type + '_' + uid fts.features.code = code } // 每一个命令一个 id putDB(fts, QC_PREFIX + code) }) delDB('customFts') } let showChangeLog = () => { putDB(pluginInfo().version, CFG_PREFIX + 'version') utools.createBrowserWindow('./helps/CHANGELOG.html', {width: 1280, height: 920}) } let isRunningAtFirstTime = () => { var historyVersion = getDB(CFG_PREFIX + 'version') if (historyVersion instanceof Object) return 'init' if (pluginInfo().version > historyVersion) return 'update' return false } // 导入默认命令 let importDefaultCommands = () => { let defaultCommands = getDefaultCommands() Object.values(defaultCommands).forEach(d => { importCommand(d) }) } // 是否含有 quickcommand 键值 let isJsonQc = obj => { var keys = ["features", "program", "cmd", "output"] if (keys.filter(x => typeof obj[x] == 'undefined').length) return false return true } // 判断是否为可导入的快捷命令 let quickCommandParser = json => { try { var qc = JSON.parse(json) } catch (error) { return false } if (isJsonQc(qc)) return { single: true, qc: qc } else if (!Object.values(qc).filter(q => !isJsonQc(q)).length) return { single: false, qc: qc } else return false } // 导入 let importCommand = file => { var pushData, clipboardText = clipboardReadText() if (!file) pushData = quickCommandParser(clipboardText) if (!pushData) { var options = file ? { type: 'file', argvs: file } : { type: 'dialog', argvs: { filters: [{ name: 'json', extensions: ['json'] }] } } options.readfile = true var fileinfo = getFileInfo(options) if (!fileinfo) return pushData = quickCommandParser(fileinfo.data) if (!pushData) return false } // 单个命令导入 if (pushData.single) { var code = pushData.qc.features.code; putDB(pushData.qc, QC_PREFIX + code); return { tags: pushData.qc.tags, code: code } // 多个命令导入 } else { for (var code of Object.keys(pushData.qc)) { putDB(pushData.qc[code], QC_PREFIX + code); } return true } } // 全部导出 let exportAll = (copy = false) => { let allQcs = getAllQuickCommands() let options = { title: '选择保存位置', defaultPath: 'quickCommand', filters: [ { name: 'json', extensions: ['json'] }, ] }; if (!isDev()) Object.keys(allQcs).forEach(k => { if (k.includes('default_')) delete allQcs[k] }) let stringifyQcs = JSON.stringify(allQcs) if (copy) utools.copyText(stringifyQcs) else window.saveFile(stringifyQcs, options); } // 清空 let clearAll = () => { quickcommand.showConfirmBox('将会清空所有自定义命令,请确认!').then(x => { if (!x) return exportAll(true) getDocs(QC_PREFIX).map(x => x._id).forEach(y => delDB(y)) importDefaultCommands(); clearAllFeatures(); showOptions(); quickcommand.showMessageBox('清空完毕,为防止误操作,已将所有命令复制到剪贴板,可通过导入命令恢复') }) } // 环境 const programs = { shell: { bin: 'bash', argv: '', ext: 'sh', color: '#89e051' }, applescript: { bin: 'osascript', argv: '', ext: 'scpt', color: '#101F1F' }, cmd: { bin: '', argv: '', ext: 'bat', codec: 'gbk', color: '#C1F12E' }, powershell: { bin: 'powershell', argv: '-NoProfile -File', ext: 'ps1', codec: utools.isWindows() ? 'gbk' : '', color: '#012456' }, python: { bin: 'python', argv: '-u', ext: 'py', codec: utools.isWindows() ? 'gbk' : '', color: '#3572A5' }, javascript: { bin: 'node', argv: '', ext: 'js', color: '#f1e05a' }, ruby: { bin: 'ruby', argv: '', ext: 'rb', color: '#701516' }, php: { bin: 'php', argv: '', ext: 'php', color: '#4F5D95' }, c: { bin: 'gcc', argv: '-o', ext: 'c', codec: utools.isWindows() ? 'gbk' : '', color: '#555555' }, csharp: { bin: 'C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\csc.exe', argv: '/Nologo', ext: 'cs', codec: 'gbk', color: '#178600' }, lua: { bin: 'lua', argv: '', ext: 'lua', color: '#000080' }, perl: { bin: 'perl', argv: '', ext: 'pl', color: '#0298c3' }, custom: { bin: '', argv: '', ext: '', codec: '', color: '#438eff' } } let getCmdsType = cmds => { try { JSON.stringify(cmds) } catch (error) { return 'illegal' } if (cmds.length == 0) return 'null' if (cmds.length == 1) { let type = cmds[0].type if (!type) return 'key' if (type == 'window' || cmds[0].minNum) return type return 'professional' } let counts = cmds.filter(x => typeof x == 'string').length return counts == cmds.length ? 'key' : 'professional' } let showCommandByType = features => { let qcType = '' let cmds = features.cmds let type = getCmdsType(cmds) if (type == 'professional') { qcType = `
专业模式
[{...}]
`; } else { let rules = cmds[0].match if (type == 'regex') { if (rules.length > 14) rules = rules.slice(0, 14) + '...'; qcType = `
正则
${rules}
`; } else if (type == 'window') { qcType += `
窗口
` if (!rules) { qcType += `所有窗口` } else if (rules.title || rules.class) { qcType += `${JSON.stringify(rules).slice(0, 14) + '...'}`; } else if (rules.app) { rules = rules.app.join(",") if (rules.length > 14) rules = rules.slice(0, 14) + '...'; rules.split(',').forEach(r => { qcType += `${r}`; }); } qcType += `
` } else if (type == 'files') { if (rules.length > 14) rules = rules.slice(0, 14) + '...'; qcType = `
文件
${rules}
`; } else { rules = features.cmds.join(",") if (rules.length > 14) rules = rules.slice(0, 14) + '...'; qcType += `
关键字
` rules.split(',').forEach(r => { qcType += `${r}`; }); qcType += `
` } } return qcType } let getEveryFeature = (fts, currentFts, tag) => { if (tag == "未分类") { if (fts.tags && fts.tags.length) return '' } else { if (!fts.tags) return '' if (!fts.tags.includes(tag)) return '' } var features = fts.features; var qcType = showCommandByType(features); var isChecked = ''; for(var c of currentFts){ if (c.code == features.code) { isChecked = 'checked'; break; } } var platformIcons if (features.platform) platformIcons = features.platform.map(x => ``) else platformIcons = ['', '', ''] return `
${features.explain}
${fts.program} | ${platformIcons.join('')}
${qcType} ${(tag == "默认" && !isDev()) ? "" : ` `} ` } // 显示设置界面 let showOptions = (tag = "默认") => { $("#options").empty().fadeIn(); var currentFts = utools.getFeatures(), customFts = getAllQuickCommands(); var allTags = ["默认"] var featureList = `
`; Object.values(customFts).forEach(fts => { // 跳过有问题的命令 try { if (fts.tags) fts.tags.map(t => !allTags.includes(t) && allTags.push(t)) featureList += getEveryFeature(fts, currentFts, tag) } catch (e) { console.log(e) } }) featureList += `
` var sidebar = ` ` var footer = `
新建命令
导入命令
分享中心
查看帮助
全部导出
启用本页
清空数据
禁用本页
` $("#options").append(sidebar + featureList + footer) } // 显示新建命令界面 let showCustomize = (readonly = false) => { $("#customize").remove(); let options = `` let allTags = [] $('.sidebar li').each(function () { let val = $(this).text() if (val != "默认" && val != "未分类") allTags.push(``) }) let customWindow = `

匹 配 关键字

说 明

环 境 标 签

变 量 输 出

脚 本 ﹢动作 ﹢按键 ?文档 格式化

${(readonly && !isDev()) ? '' : ''}

` $("#options").append(customWindow) $("#icon").attr('src', 'logo/quickcommand.png'); getSpecialVars() createEditor() createProgramSelect2('40%') createTypeSelect2('40%') var singleSelectOpt = { width: '40%', minimumResultsForSearch: Infinity, dropdownParent: $("#customize") } $('#output').select2(singleSelectOpt); singleSelectOpt.placeholder = "插入特殊变量" $('#vars').select2(singleSelectOpt); $('#tags').select2({ width: '40%', placeholder: "选择或添加标签, 最多3个", tags: true, allowClear: true, tokenSeparators: [',', ' '], maximumSelectionLength: 3, dropdownParent: $("#customize") }).on("select2:unselecting", e => { (e.params.args.data.text == "默认") && !isDev() && e.preventDefault(); }).on("select2:selecting", e => { (e.params.args.data.text == "默认" || e.params.args.data.text == "未分类") && !isDev() && e.preventDefault(); }) } let getSelect2Option = (data, width, dropdownAutoWidth = false) => { var options = { data: data, minimumResultsForSearch: Infinity, dropdownParent: $("#customize"), dropdownAutoWidth: dropdownAutoWidth, escapeMarkup: markup => markup, templateSelection: data => data.text, templateResult: data => data.html } if (width) options.width = width return options } let getTypeSheet = () => { return [ { id: "key", text: "关键字", html: "关键字
在主输入框输入对应关键字进入插件,最通用的一种模式,关键字可以设置多个
" }, { id: "regex", text: "正则/划词", html: "正则/划词
正则匹配主输入框文本或唤出超级面板时选中的文本,可以获取输入框文本或选中文本作为变量
" }, { id: "window", text: "窗口/进程", html: "窗口/进程
匹配呼出uTools前或唤出超级面板时的活动窗口,可以获取窗口的信息或文件夹路径作为变量
" }, { id: "files", text: "复制/选中文件", html: "复制/选中文件
匹配拖入主输入框的文件或唤出超级面板时选中的文件,可以获取复制及选中的文件信息作为变量
" }, { id: "professional", text: "专业模式", html: "专业模式
通过json格式的配置实现同时匹配关键字、窗口、文件甚至图片,或者指定文件数量、窗口类等
" } ] } let createTypeSelect2 = (width = false) => { var data = getTypeSheet() $('#type').select2(getSelect2Option(data, width)); } let createProgramSelect2 = (width, dropdownAutoWidth = false) => { var programStyled = p => `${p}` var data = [{ id: "quickcommand", text: 'quickcommand', html: programStyled('quickcommand') }] data = data.concat(Object.keys(programs).map(x => { return { id: x, text: x, html: programStyled(x) } })) $('#program').select2(getSelect2Option(data, width, dropdownAutoWidth)); } let createEditor = () => { window.editor = CodeMirror.fromTextArea(document.getElementById("cmd"), { lineNumbers: true, matchBrackets: true, // lineWrapping: true, autoCloseBrackets: true, styleActiveLine: true, keyMap: "sublime", theme: utools.isDarkColors() ? 'material-darker' : "mdn-like", extraKeys: { "Alt-Enter": () => { $('.CodeMirror').hasClass('CodeMirror-fullscreen') && $('.CodeMirror').removeClass('CodeMirror-fullscreen') || $('.CodeMirror').addClass('CodeMirror-fullscreen') }, "Ctrl-B": () => { runCurrentCommand() }, "Ctrl-S": () => { SaveCurrentCommand() }, "Ctrl-Q": () => { quitCurrentCommand() }, "Shift-Alt-F": () => { beautifyCode() }, "Alt-Up": "swapLineUp", "Alt-Down": "swapLineDown", "Shift-Alt-Down": "duplicateLine" } }); window.editor.on("change", showHint); window.editor.setOption("mode", 'javascript'); } showHint = () => { editor.showHint({ completeSingle: false }); } let beautifyCode = () => { if ($("#customize").is(":parent")) { var cmd = window.editor.getValue() switch ($("#program").val()) { case "quickcommand": case "javascript": window.editor.setValue(js_beautify(cmd, { brace_style: "collapse,preserve-inline" })) break; case "python": py_beautify(cmd, data => { window.editor.setValue(data) }) break; default: quickcommand.showMessageBox('暂不支持该语言的格式化', 'error') break; } } } // 获取特殊变量 let getSpecialVars = () => { var specialVars = [] $("#vars option").each(i => { var selector = $("#vars option").eq(i) if (!selector.prop('disabled')) specialVars.push(selector.val()) }) localStorage['specialVars'] = specialVars } let typeCheck = () => { var type = $("#type").val(); switch (type) { case 'key': $("#ruleWord").html("关键字"); $(".var.regex, .var.window, .var.files").prop("disabled", true) $("#rule").prop("placeholder", '多个关键字用逗号隔开'); break; case 'regex': $("#ruleWord").html("正 则"); $(".var.window, .var.files").prop("disabled", true) $(".var.regex").prop("disabled", false) $("#rule").prop("placeholder", '匹配文本的正则,如 /.*?\\.exe$/i'); break; case 'files': $("#ruleWord").html("正 则"); $(".var.regex, .var.window").prop("disabled", true) $(".var.files").prop("disabled", false) $("#rule").prop("placeholder", '匹配文件的正则,如 /.*?\\.exe$/i'); break; case 'window': $("#ruleWord").html("进 程"); $(".var.regex, .var.files").prop("disabled", true) $(".var.window").prop("disabled", false) $("#rule").prop("placeholder", '多个窗口进程逗号隔开,留空匹配所有窗口'); break; case 'professional': $("#ruleWord").html("配 置"); $(".var.regex, .var.window, .var.files").prop("disabled", false) $("#rule").prop("placeholder", '等效于 features.cmds'); let sample = `["关键词",{"type":"img","label":"图片匹配"},{"type":"files","label":"文件匹配","fileType":"file","match":"/aaa/","minLength":1,"maxLength":99},{"type":"regex","label":"文本正则匹配","match":"/bbb/i","minLength":1,"maxLength":99},{"type":"over","label":"无匹配时","exclude":"/ccc/i","minLength":1,"maxLength":99},{"type":"window","label":"窗口动作","match":{"app":["ddd.app","eee.exe"],"title":"/fff/","class":["ggg"]}}]` !$('#rule').val() && $('#rule').val(sample) default: break; } getSpecialVars() } let clearAllFeatures = () => { for (var fts of utools.getFeatures()) { utoolsFull.removeFeature(fts.code) } } let hasCustomIcon = () => { var src = $("#icon").attr('src'); var iconame = $("#iconame").val(); return /data:image\/\w+;base64,/.test(src) || iconame } let programCheck = () => { let mode = $('#program').val(); $('.customscript').hide(); $('.quickactions').hide(); $('#scptarg').show(); $('#showInTerm').prop("disabled", false); if (!hasCustomIcon()) $("#icon").attr('src', `logo/${mode}.png`); switch (mode) { case 'custom': $('.customscript').show(); var customext = $('#customext').val() customext && (mode = highlightIfKnown(customext)) break; case 'quickcommand': $('.quickactions').show(); $('#scptarg').hide(); $('#showInTerm').prop("disabled", true); mode = 'javascript'; break; case 'csharp': case 'c': mode = 'text/x-' + mode break; case 'python': getPythonMods() break; case 'cmd': getCmdCommand() break; case 'shell': getShellCommand() break; default: break; } window.editor.setOption("mode", mode); } // 合规性校验 let cmdCheck = (type, cmd) => { var blacklist switch (type) { case 'key': blacklist = cmd.match(/{{input}}|{{SelectFile}}|{{pwd}}|{{WindowInfo}}|{{MatchedFiles}}/g) break; case 'regex': blacklist = cmd.match(/{{SelectFile}}|{{WindowInfo}}|{{pwd}}|{{MatchedFiles}}/g) if (/^(|\/)\.[*+](|\/)$/.test($('#rule').val())) return quickcommand.showMessageBox('正则匹配 .* 和 .+ 已被uTools禁用!', 'error') break; case 'window': blacklist = cmd.match(/{{input}}|{{MatchedFiles}}/g) break; case 'files': blacklist = cmd.match(/{{input}}|{{SelectFile}}|{{pwd}}|{{WindowInfo}}/g) break; default: break; } if (blacklist) { quickcommand.showMessageBox(`当前模式无法使用${Array.from(new Set(blacklist)).join("、")}`, 'error') return false } else { return true } } // 开关 $("#options").on('change', 'input[type=checkbox]', function () { var customFts = getAllQuickCommands(), code = $(this).parents('tr').attr('id') if (!utoolsFull.removeFeature(code)) { utoolsFull.setFeature(customFts[code].features); } }); // 底部功能按钮 $("#options").on('click', '.footBtn', function () { switch ($(this).attr('id')) { case 'viewHelps': utools.createBrowserWindow('./helps/HELP.html', {width: 1280, height: 920}); break; case 'getShares': getSharedQCFromYuQue(); break; case 'add': showCustomize(); $("#customize").animate({ top: '0px' }); break; case 'import': var success = importCommand() if (success) { if (success instanceof Object) locateToCode(success.tags, success.code) else showOptions() quickcommand.showMessageBox("导入成功") } else if (success == false) { quickcommand.showMessageBox("导入失败,格式错误", "error") } break; case 'enableAll': $(".checked-switch:not(:checked)").click(); break; case 'disableAll': $(".checked-switch:checked").click(); break; case 'exportAll': exportAll(); break; case 'clear': clearAll(); break; } }) let editCurrentCommand = data => { let code = data.features.code let platform = data.features.platform let readonly = false let extraInfo = { authorName: data.authorName, authorId: data.authorId, fromShare: data.fromShare } if (data.tags && data.tags.includes("默认")) readonly = true showCustomize(readonly); $('#customize').data('extraInfo', extraInfo) data.tags && $('#tags').val(data.tags).trigger('change') platform && ["win32", "darwin", "linux"].map(x => (!platform.includes(x) && $(`#${x}`).addClass('disabled'))) let cmds = data.features.cmds let type = getCmdsType(cmds) $('#type').val(type).trigger("change") if (type == 'professional') { $('#rule').val(JSON.stringify(cmds)) } else { cmds = cmds[0] if (type == 'regex' || type == 'files') { $('#rule').val(cmds.match); } else if (type == 'window') { if (!cmds.match) $('#rule').val(''); else if (cmds.match.title || cmds.match.class) $('#rule').val(JSON.stringify(cmds.match)); else $('#rule').val(cmds.match.app); } else { $('#type').val('key').trigger("change") $('#rule').val(data.features.cmds.toString()); } } $('#code').val(code); $('#program').val(data.program).trigger("change"); $('#output').val(data.output).trigger("change"); $('#desc').val(data.features.explain); $('#scptarg').val(data.scptarg); $("#icon").attr('src', data.features.icon); let mode = data.program; if (mode == 'custom') { $('#custombin').show().val(data.customOptions.bin); $('#customarg').show().val(data.customOptions.argv); $('#customext').show().val(data.customOptions.ext); $('#customcodec').show().val(data.customOptions.codec); } typeCheck(); programCheck(); // 分段载入,保障动画流畅 window.editor.setValue(data.cmd.slice(0, 2000)); $("#customize").animate({ top: '0px' }, () => { window.editor.replaceRange(data.cmd.slice(2000), {line: Infinity}); }); } // 编辑 $("#options").on('click', '.editBtn', function () { let code = $(this).parents('tr').attr('id') let data = getDB(QC_PREFIX + code) editCurrentCommand(data) }) // 添加模拟按键 $("#options").on('click', '#addKey', function () { $("#addKey").text("▶ 录制中").addClass('record') quickcommand.showMessageBox('开始录制按键,可连续录制', 'info') Mousetrap.record(sequence => { sequence.forEach(s => { var keys = s if (s.includes('+') && s.length > 1) keys = s.split('+').reverse().map(x=>x.trim().replace('meta', 'command')).join(`", "`) window.editor.replaceSelection(`keyTap("${keys}")\n`) }) $("#addKey").text("﹢按键").removeClass('record') }); }) // quickCommand的帮助 $("#options").on('click', '#showHelp', function () { utools.createBrowserWindow('./helps/quickcommand.html', {width: 1280, height: 920}) }) // 添加动作 $("#options").on('click', '#addAction', function () { var html = ` 加引号 ` Swal.fire({ title: "预设动作", onBeforeOpen: () => { $('#actionType').change(function () { $('#actionArgs').attr('placeholder', $(this).find(`[value=${$(this).val().replace('.', '\\.')}]`).attr('args')) }) }, html: html, showCancelButton: true, preConfirm: () => { var actionType = $("#actionType").val() var actionArgs = $("#actionArgs").val().replace(/\\/g, '\\\\') if ($("#isString").is(':checked')) actionArgs = `"` + actionArgs + `"` var action = `${actionType}(${actionArgs})` if (actionType == 'utools.ubrowser.goto') action += `.run()` window.editor.replaceSelection(`${action}\n`) } }) }) let setYuQueToken = async () => { let yuQueToken = await quickcommand.showInputBox(["请输入 Token"]) if (!yuQueToken) return yuQueToken = yuQueToken[0] yuQueClient.defaults.headers['X-Auth-Token'] = yuQueToken try { let res = await yuQueClient('user') let extraInfo = { yuQueToken: yuQueToken, authorId: res.data.data.account_id, authorName: res.data.data.name } putDB(extraInfo, CFG_PREFIX + 'extraInfo') quickcommand.showMessageBox("设置成功~") } catch (e) { quickcommand.showMessageBox('Token 校验失败', "error") } } let createShareMenu = jsonQc => { let menu = ['复制到剪贴板', '导出到文件', '', '设置 Token'] let extraInfo = getDB(CFG_PREFIX + 'extraInfo') if (jsonQc.authorId) { if (jsonQc.authorId == extraInfo.authorId) menu[2] = '更新分享' else if (jsonQc.fromShare) menu[2] = '评论' else menu[2] = '分享自:' + jsonQc.authorName } else { if (extraInfo.yuQueToken) menu[2] = '分享命令' else menu[2] = '我要分享' } return menu } // 分享相关 const yuQueShareVars = { imgBedApi: 'https://imgkr.com/api/v2/files/upload', imgBedBaseLink: 'https://imgkr.cn-bj.ufileos.com/', yuQueImgBedBaseLink: 'https://cdn.nlark.com/yuque/', releaseRepo: 'fofolee/qcreleases', shareRepo: 'fofolee/qcshares', shareLock: false } // 导出 $("#options").on('click', '.exportBtn', async function () { var code = $(this).parents('tr').attr('id') var jsonQc = getDB(QC_PREFIX + code) var stringifyQc = JSON.stringify(jsonQc, null, 4) var choise = await quickcommand.showButtonBox(createShareMenu(jsonQc)) switch (choise.text) { case '复制到剪贴板': utools.copyText(stringifyQc) && quickcommand.showMessageBox('已复制到剪贴板') break; case '导出到文件': window.saveFile(stringifyQc, { title: '选择保存位置', defaultPath: `${jsonQc.features.explain}.json`, filters: [ { name: 'json', extensions: ['json'] }, ] }) break; case '分享命令': case '更新分享': if (yuQueShareVars.shareLock) { quickcommand.showMessageBox('分享速度太快了,请稍候', 'warning') } else { yuQueShareVars.shareLock = true jsonQc = await updateImgLink(jsonQc) var result = await shareQCToYuQue(jsonQc) yuQueShareVars.shareLock = false result && quickcommand.showMessageBox('分享成功,等待发布后即可在分享中心直接下载') } break; case '我要分享': utools.createBrowserWindow('./helps/HELP.html?#分享命令', {width: 1280, height: 920}) break; case '评论': utools.shellOpenExternal(`https://www.yuque.com/${yuQueShareVars.releaseRepo}/${code}`) break; case '设置 Token': await setYuQueToken() break; } }) let updateImgLink = async jsonQc => { let icon = jsonQc.features.icon if (!jsonQc.imgLink && icon.includes('base64')) { try { if (icon.length > 2000) icon = await getCompressedIco(icon) jsonQc.features.icon = icon let res = await quickcommand.uploadFile(yuQueShareVars.imgBedApi, dataURLtoFile(icon, jsonQc.features.code + '.png')) jsonQc.imgLink = res.data.data } catch (error) { console.log(error); } } return jsonQc } let dataURLtoFile = (dataurl, filename) => { let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); while (n--) { u8arr[n] = bstr.charCodeAt(n); } return new File([u8arr], filename, { type: mime }); } // 一键分享到语雀 let shareQCToYuQue = async jsonQc => { let extraInfo = getDB(CFG_PREFIX + 'extraInfo') if (!extraInfo.yuQueToken) return quickcommand.showMessageBox("请先设置 Token,点击底部「查看帮助」可查看 Token 设置方法", "error") jsonQc.authorId = extraInfo.authorId jsonQc.authorName = extraInfo.authorName let stringifyQc = JSON.stringify(jsonQc, null, 4) if (stringifyQc.length > 5000000) return quickcommand.showMessageBox('命令大小超过5M无法分享,请检查图标或脚本内容是否过大', "error") let type = jsonQc.features.cmds[0].type || 'key' let tags = jsonQc.tags ? jsonQc.tags.join(' ') : "" let typeDescription = getTypeSheet().filter(x => x.id == type)[0].text let custom_description = { authorName: jsonQc.authorName, program: jsonQc.program, type: typeDescription, platform: jsonQc.features.platform || ['win32', 'darwin', 'linux'], tags: tags } let parameters = { title: jsonQc.features.explain, slug: jsonQc.features.code, public: 1, format: "markdown", body: '```json\n' + stringifyQc + '\n```', custom_description: JSON.stringify(custom_description) } if (jsonQc.imgLink) parameters.cover = jsonQc.imgLink.replace(yuQueShareVars.imgBedBaseLink, yuQueShareVars.yuQueImgBedBaseLink) yuQueClient.defaults.headers['X-Auth-Token'] = extraInfo.yuQueToken let res, repo = extraInfo.authorId == 1496740 ? yuQueShareVars.releaseRepo : yuQueShareVars.shareRepo try { res = await yuQueClient.post(`repos/${repo}/docs`, parameters) if (!res.data.data) return quickcommand.showMessageBox("分享失败,不知道为啥", "error") let docId = res.data.data.id res = await yuQueClient.put(`repos/${repo}/docs/${docId}`, parameters) if (!res.data.data) return quickcommand.showMessageBox("分享失败,不知道为啥", "error") putDB(jsonQc, jsonQc.features.code); return jsonQc } catch (error) { return quickcommand.showMessageBox(error, "error") } } let getSharedQCFromYuQue = async () => { $('#options').hide() let extraInfo = getDB(CFG_PREFIX + 'extraInfo') if (extraInfo.yuQueToken) yuQueClient.defaults.headers['X-Auth-Token'] = extraInfo.yuQueToken let res = await yuQueClient(`repos/${yuQueShareVars.releaseRepo}/docs`) let description, platform = window.processPlatform let docs = res.data.data .filter(d => { try { return JSON.parse(d.custom_description).platform.includes(platform) } catch (error) { console.log(error) } }) .sort((x, y) => { if (y.updated_at > x.updated_at) return 1 else return -1 }) .map(d => { description = JSON.parse(d.custom_description) return { title: d.title, description: ` ${description.authorName}   ${description.program}   ${description.type}   ${description.tags}   ${d.updated_at.split('T')[0]}`, slug: d.slug, icon: d.cover ? d.cover.replace(yuQueShareVars.yuQueImgBedBaseLink, yuQueShareVars.imgBedBaseLink) : `logo/${description.program}.png` } }) let choise = await quickcommand.showSelectList(docs, { optionType: 'json', showCancelButton: true }) if (choise) { let doc = await yuQueClient(`repos/${yuQueShareVars.releaseRepo}/docs/${choise.slug}?raw=1`) let body = doc.data.data.body let stringifyQc = body.match(/```json([\s\S]*)```/)[1] let qc = JSON.parse(stringifyQc) qc.fromShare = true $('#options').show() editCurrentCommand(qc) $('#customize').data('returnShare', true) } else { $('#options').show() $('#customize').removeData('returnShare') } utools.setExpendHeight(600) } $("#out").on('click', '#importSharedQc', function () { importCommand() ? quickcommand.showMessageBox("导入成功") : quickcommand.showMessageBox("导入失败,格式错误", "error") showOptions() utools.setExpendHeight(600) $('#out').empty() }) // 删除 $("#options").on('click', '.delBtn', function () { quickcommand.showConfirmBox('删除这个快捷命令').then(x => { if (!x) return var code = $(this).parents('tr').attr('id') utools.copyText(JSON.stringify(getDB(QC_PREFIX + code))) delDB(QC_PREFIX + code) utoolsFull.removeFeature(code); var currentTag = $('.currentTag').text() if ($('#featureList tr').length == 1) currentTag = "默认" showOptions(currentTag); quickcommand.showMessageBox('删除成功,为防止误操作,已将删除的命令复制到剪贴板') }) }) // 选择图标 $("#options").on('click', '#icon', async function () { var options = { buttonLabel: '选择', properties: ['openFile'] } var file = getFileInfo({ type: 'dialog', argvs: options, readfile: false }) if (file) { $("#iconame").val(file.name); let src = await getBase64Ico(file.path); $("#icon").attr('src', src); } }) let SaveCurrentCommand = async () => { if ($('#tags').is(":parent")) { var type = $('#type').val(), code = $("#code").val(), tags = $('#tags').val(), rule = $('#rule').val(), cmd = window.editor.getValue(); if (tags && tags.includes("默认") && !isDev()) return if (type != "window" && !rule) return quickcommand.showMessageBox(`${$('#ruleWord').text().replace(" ", "")} 不能留空!`, 'error') if (!cmdCheck(type, cmd)) return if (!code) { // 生成唯一code var uid = Number(Math.random().toString().substr(3, 3) + Date.now()).toString(36); var code = `${type}_${uid}`; } var output = $('#output').val(), scptarg = $('#scptarg').val(), program = $('#program').val(), desc = $('#desc').val(), icon = $("#icon").attr('src'), hasSubInput; if (!desc) desc = ' '; if (type == 'key') { cmds = rule.split(",").map(x => x.trim()) } else if (type == 'regex') { if (!/^\/.*?\/[igm]*$/.test(rule)) { rule = "/" + rule + "/" } cmds = [{ "label": desc, "type": "regex", "match": rule, "minNum": 1 }]; } else if (type == 'window') { var cmdOfWin = { "label": desc, "type": "window" } if (rule) { try { cmdOfWin.match = JSON.parse(rule) } catch (error) { cmdOfWin.match = { "app": rule.split(',') } } } cmds = [cmdOfWin]; } else if (type == 'files') { if (!/^\/.*?\/[igm]*$/.test(rule)) { rule = "/" + rule + "/" } cmds = [{ "label": desc, "type": "files", "match": rule, "minNum": 1 }]; } else if (type == 'professional') { try { cmds = JSON.parse(rule) } catch (error) { return quickcommand.showMessageBox(`配置的格式有误,请核查!`, 'error') } } // 需要子输入框 if (cmd.includes('{{subinput')) { hasSubInput = true; } else { hasSubInput = false; } // platform var platform = [] $('.platform').not('.disabled').each(function () { platform.push($(this).attr('id')) }) // 添加特性 var extraInfo = $('#customize').data('extraInfo') var pushData = { features: { "code": code, "explain": desc, "cmds": cmds, "icon": icon, "platform": platform }, program: program, cmd: cmd, output: output, hasSubInput: hasSubInput, scptarg: scptarg } if (extraInfo) { Object.assign(pushData, extraInfo) // 通过模拟访问页面来统计下载量 extraInfo.fromShare && utools.ubrowser.goto(`https://www.yuque.com/${yuQueShareVars.releaseRepo}/${code}`).run({ show: false }) } if (tags) pushData.tags = tags if (program == 'custom') { pushData.customOptions = { "bin": $('#custombin').val(), "argv": $('#customarg').val(), "ext": $('#customext').val(), 'codec': $('#customcodec').val() } } putDB(pushData, QC_PREFIX + code); $("#customize").animate({ top: '100%' }, () => { $("#customize").empty() if ($('#customize').data('returnShare')) { getSharedQCFromYuQue() } else { locateToCode(pushData.tags, code) let checkSwitch = $(`#${code} .checked-switch`) checkSwitch.click() checkSwitch.is(':checked') || checkSwitch.click() } }); } } // 保存后标签跳转处理 let locateToCode = (tags, code) => { let redirectTag let currentTag = $('.currentTag').text() // let AllTags = Array.from($('.sidebar li')).map(x => x.innerText) if (tags && tags.length) { if (tags.includes(currentTag)) { redirectTag = currentTag } else { redirectTag = tags[0] } } else { redirectTag = "未分类" } showOptions(redirectTag); location.href = '#' + code } // 显示运行结果 let showRunResult = (content, raw, success) => { var options, position, showClass, hideClass, maxlength = 100000 if ($("#vars").is(":parent")) { position = 'top' showClass = 'fadeInDownWindow' hideClass = 'fadeOutUpWindow' } else { position = 'bottom' showClass = 'fadeInUpWindow' hideClass = 'fadeOutDownWindow' } var preView = () => { var result = $('#swal2-content').text() result = htmlEncode(result, raw) $(".swal2-content").css("width", "100%") $('#swal2-content').html(`
${result}
`) $('.swal2-popup').addClass('swal2-toast') } var contlength = content.length if (contlength > maxlength) content = content.slice(0, maxlength - 100) + `\n\n...\n${contlength - maxlength - 100} 字省略\n...\n\n` + content.slice(contlength - 100) content += '\n' var outputchannel = $("#swal2-content > pre") if (outputchannel.is(":parent")) { outputchannel.append(htmlEncode(content, raw)) } else { options = { onBeforeOpen: preView, icon: success ? "success" : "error", text: content, position: position, width: 800, showConfirmButton: true, showClass: { popup: showClass }, hideClass: { popup: hideClass } } Swal.fire(options) } } let replaceTempInputVals = async cmd => { let tempInputVals = [] let specilaVals = ['input', 'subinput', 'pwd', 'SelectFile', 'WindowInfo', 'MatchedFiles'] specilaVals.forEach(x => { let m = cmd.match(new RegExp('{{' + x + '.*?}}', 'g')) m && m.forEach(y => tempInputVals.includes(y) || tempInputVals.push(y)) }) if (!tempInputVals.length) return cmd let inputs = await quickcommand.showInputBox(tempInputVals, '需要临时为以下变量赋值') tempInputVals.forEach((t, n) => { cmd = cmd.replace(new RegExp(t, 'g'), inputs[n]) }) return cmd } let runCurrentCommand = async () => { if ($("#customize").is(":parent")) { var cmd = window.editor.getValue() cmd = special(cmd) cmd = await replaceTempInputVals(cmd) var program = $("#program").val() var output = $("#output").val() var terminal = false var raw = true switch (output) { case "html": raw = false break; case "terminal": terminal = true break; case "ignore": utools.hideMainWindow() break; } if (program == "quickcommand") { runCodeInVm(cmd, (stdout, stderr) => { if (stderr) return showRunResult(stderr, raw, false) showRunResult(stdout, raw, true) }); } else { var option = programs[program] if (program == "custom") option = { "bin": $('#custombin').val(), "argv": $('#customarg').val(), "ext": $('#customext').val(), 'codec': $('#customcodec').val() } option.scptarg = $('#scptarg').val() runCodeFile(cmd, option, terminal, (stdout, stderr) => { if (terminal) return if (stderr) return showRunResult(stderr, raw, false) showRunResult(stdout, raw, true) }) } } } let killCurrentCommand = () => { } let quitCurrentCommand = () => { if ($("#customize").is(":parent") && $("#featureList").is(":parent")) { $("#customize").animate({ top: '100%' }); $("#customize").empty() if ($('#customize').data('returnShare')) getSharedQCFromYuQue() } } let highlightIfKnown = ext => { var lang = Object.keys(programs).filter(p => programs[p].ext == ext) if (lang.length) { if (lang[0] == 'python') getPythonMods() window.editor.setOption("mode", lang[0]) return lang[0] } } let showCodeEditor = file => { var customWindow = `
运 行 格式化 ?文档
` $("#options").html(customWindow) createEditor() $(".CodeMirror").addClass('CodeMirror-coderunner') $("#customize").css({ top: '0px', padding: '0px' }); $("span.customscript > input").css({"height": "30px"}) var history = getDB(CFG_PREFIX + 'codeHistory') createProgramSelect2(140, true) if (file) { var fileinfo = getFileInfo({ type: 'file', argvs: file, readfile: true }) window.editor.setValue(fileinfo.data) var program = Object.keys(programs).filter(x => `.${programs[x].ext}` == fileinfo.ext) if (program) $('#program').val(program[0]).trigger('change') // runCurrentCommand() } else if(history.program){ window.editor.setValue(history.cmd) $('#program').val(history.program).trigger('change') $('#scptarg').val(history.scptarg) var custom = history.customoptions if (history.program = 'custom' && custom) { $('#custombin').val(custom.custombin) $('#customarg').val(custom.customarg) $('#customext').val(custom.customext) $('#customcodec').val(custom.customcodec) } } programCheck() $("#options").show() } // 输出搜索 let showSearchBox = () => { if ($('#options').is(':hidden') && $('#out').is(":parent")) { $('#out').append(`
`) $('#outputSearch').animate({ opacity: 1, top: '10px', }) document.getElementById('find-next').onclick = () => { utools.findInPage($('#outputSearch > input').val()) } document.getElementById('find-prev').onclick = () => { utools.findInPage($('#outputSearch > input').val(), { forward: false }) } document.getElementById('find-close').onclick = () => { utools.stopFindInPage() $('#outputSearch').animate({ opacity: 0, top: '-30px', }, () => { $('#outputSearch').remove() }) } } } // 切换TAGS $("#options").on('click', '.sidebar li', function () { showOptions($(this).text()); }) // 运行 $("#options").on('click', '.cmdBtn.run, #runCode', function () { runCurrentCommand() }) // 配置栏扩大 $("#options").on('click', '#expandBtn', function () { let placeholder = $('#rule').prop('placeholder') let rule = $('#rule').val() try { rule = JSON.stringify(JSON.parse(rule), null, 4) } catch (error) { } quickcommand.showTextAera(placeholder, rule).then(x => { try { x = JSON.stringify(JSON.parse(x)) } catch (error) { } $('#rule').val(x) }) }) // 格式化 $("#options").on('click', '#beautifyCode', function () { beautifyCode() }) // 取消 $("#options").on('click', '.cmdBtn.cancel', function () { quitCurrentCommand() }) // 保存 $("#options").on('click', '.cmdBtn.save', function () { SaveCurrentCommand() }) // 语言选项改变时 $("#options").on('change', '#program', function () { programCheck() }) // 变量选项改变时 $("#options").on('change', '#vars', function () { $("#vars").css({'color':'black'}) window.editor.replaceSelection($("#vars").val()); }) $("#options").on('change', '#action', function () { $("#action").css({ 'color': 'black' }) }) // 方式选项改变时 $("#options").on('change', '#type', function () { // resetVars(); typeCheck(); }) $("#options").on('change', '#customext', function () { highlightIfKnown($('#customext').val()) }) // 平台按钮 $("#options").on('click', '.platform', function () { if ($(this).hasClass('disabled')){ $(this).removeClass('disabled') } else { if ($('.disabled').length == 2) quickcommand.showMessageBox('至少保留一个平台', 'error') else $(this).addClass('disabled') } }) Mousetrap.bind('ctrl+s', () => { SaveCurrentCommand() return false }); Mousetrap.bind('ctrl+q', () => { quitCurrentCommand() return false }); Mousetrap.bind('ctrl+b', () => { runCurrentCommand() return false }); Mousetrap.bind('ctrl+f', () => { showSearchBox() return false }); }()