新增built脚本
@@ -1,296 +0,0 @@
|
||||
## V2.3.4
|
||||
- 有子输入框的情况下,如果5s内复制了内容再呼出,会自动执行复制的内容
|
||||
- 完善脚本的报错信息,更容易定位到错误点
|
||||
- 修复4k屏下搜索到的图标模糊的 bug
|
||||
- 修复暗黑模式下的一些配色 bug
|
||||
- 修复了如果通过 newcommand 创建命令,会出现页面无法点击的 bug
|
||||
- 修复 quickcommand.runInTerminal 当参数值含有双引号时命令执行不正常的 bug
|
||||
|
||||
## V2.3.3
|
||||
- 修复新安装插件的用户,如不填写标签会无法保存的 bug
|
||||
|
||||
## V2.3.2
|
||||
- 修复 icon8s 图标无法搜索的 bug
|
||||
|
||||
## v2.3.1
|
||||
- win 下样式 bug 修复
|
||||
|
||||
## v2.3.0
|
||||
- 新增快捷面板 beta
|
||||
- 将某一个标签下的命令以面板形式展现
|
||||
- 可快速实现网址导航面板、软件启动面板之类的功能
|
||||
- 可以结合分享中心的`收藏网址`、`收藏文件`使用
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
- 新增 quickcommand.runInTermial
|
||||
|
||||
- 导入命令式会弹出选择窗口,而不是直接导入
|
||||
|
||||
- 代码格式调整
|
||||
|
||||
- 修复一个样式 bug
|
||||
|
||||
## v2.2.0
|
||||
|
||||
- 支持通过网络路径,本地路径和在线搜索三种形式来设置命令图标
|
||||

|
||||
- 新增两个特殊变量 {{type}} 和 {{payload}},适用于非 quickcommand 环境下的专业模式
|
||||
- 增加 quickcommand.runVbs,可以运行 vbs 脚本
|
||||
- 支持手动设置脚本保存时的文件编码和运行输出结果的解码方式
|
||||

|
||||
- 修复分享中心调整图标显示bug
|
||||
- 新增`NewCommand`功能,方便快速进入新建命令界面
|
||||
- 去除`运行脚本`功能
|
||||
- 在分享中心上线三个新的快捷命令,分别为`收藏网址`,`朗读`, 和新版的`运行脚本`
|
||||
|
||||
## v2.1.1
|
||||
|
||||
- 在新建命令支持选择任意文件作为图标
|
||||
- 当选择 `.PNG` `.JPG` `.ICO` 等文件时,则以图片作为图标
|
||||
- 当选择 `.EXE` `.APP` 时,则以程序的图标作为图标
|
||||
- 当选择其他文件时,则以该文件的默认图标作为图标
|
||||
- 新增 `quickcommand.enterData` 用以获取进入插件时匹配的类型和数据,用法详见文档
|
||||
- 提供一个示例,作为`专业模式`下`配置`的默认值
|
||||
|
||||
## v2.1.0
|
||||
|
||||
### 新增功能
|
||||
|
||||
- 输出选项添加`忽略输出并保留窗口`
|
||||
- 添加`专业模式`的匹配选项
|
||||
- 可以通过 json 格式的配置实现同时匹配关键字、窗口、文件甚至图片
|
||||
- 可以实现指定文件数量、窗口类等
|
||||
- json 格式配置和插件开发的 `features.cmds` 一致
|
||||
- 配置处可以点击旁边的按钮全屏,方便填写 json 格式的配置
|
||||
- `quickcommand.showTextAera` 新增一个参数可以设置默认文本值
|
||||
|
||||

|
||||

|
||||
|
||||
### BUG FIX
|
||||
|
||||
- 修复图标比例非 1:1 时排版出错的 bug
|
||||
- 修复某些情况下无法启动 windows terminal 的问题
|
||||
- 修复`MatchedFiles`匹配的文件路径中包含`$$`时会被处理成`$`的问题
|
||||
|
||||
## v2.0.1
|
||||
|
||||
- 修复当某个命令在新版本不兼容时,导致所有命令无法显示的 bug
|
||||
|
||||
## v2.0.0
|
||||
|
||||
本次带来了 海量更新,请仔细看完更新日志~
|
||||
|
||||
**更新后第一次进入插件会显示空白一段时间,是在对老版本的命令做兼容处理,属于正常现象**
|
||||
|
||||
### 新增功能
|
||||
|
||||
#### 分享中心
|
||||
|
||||
可以在线下载分享的命令
|
||||
|
||||

|
||||
|
||||
#### 暗黑模式
|
||||
|
||||
全面兼容暗黑模式
|
||||
|
||||
#### 内置命令
|
||||
|
||||
内置`文本处理`,`find GUI`,`执行 shell 命令`等多个实用快捷命令,均使用新版本新增功能实现
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
#### RunCode
|
||||
|
||||
新增一个脚本编辑器,可以直接进行脚本的编辑及测试
|
||||
|
||||
支持通过关键词或文件匹配方式进入
|
||||
|
||||

|
||||
|
||||
#### 帮助
|
||||
|
||||
新增一篇[帮助文档](./HELP.html)
|
||||
|
||||
#### 多开
|
||||
|
||||
分离插件后可以多开
|
||||
|
||||
### 新增特性
|
||||
|
||||
#### 匹配
|
||||
|
||||
- 新增文件匹配
|
||||
|
||||
#### 环境
|
||||
|
||||
##### quickcommand
|
||||
|
||||
- `模拟动作`模式改名为`quickcommand`
|
||||
- 在 vm2 内运行,与插件环境隔离
|
||||
- 无需本机预装环境,支持使用 nodejs/electron/utools 的 api
|
||||
- 支持**通过列表、多输入框、多按钮、文本框**等多种形式获得用户的输入
|
||||
- 现在和其他脚本一样可以针对输出进行不同处理或显示
|
||||
- 封包了一些实用功能
|
||||
- 现在添加按键通过监听按键进行添加,不再需要手动添加
|
||||
- 添加更多的预设动作
|
||||
- 重写延时函数,解决 utools 隐藏后延时不准的问题(其实是 electron 的问题)
|
||||
- [API 文档](./quickcommand.html)
|
||||
|
||||

|
||||
|
||||
##### 其他脚本
|
||||
|
||||
- 新增支持运行`C# `,需要安装 .net framework v4.0.30319
|
||||
- `C`脚本, 需要安装 gcc 并加入环境变量
|
||||
- windows 下可以通过 wsl 运行`shell`脚本
|
||||
- 可以运行带参数的脚本
|
||||
|
||||
##### custom
|
||||
|
||||
- 环境选择`custom`可以自定义解释器的路径、参数、脚本后缀以及输出解码的方式
|
||||
|
||||
#### 标签
|
||||
|
||||
- 支持给命令添加标签,并在配置界面按标签进行分类显示
|
||||
- 最多支持 3 个标签
|
||||
|
||||
#### 变量
|
||||
|
||||
- 新增` {{MatchedFiles}} `用来获取匹配的文件
|
||||
- `{{MatchedFiles}}` 及` {{WindowInfo}} `支持获取特殊的键值,比如通过 `{{WindowInfo.id}}`获取匹配的窗口 id ,通过`{{MatchedFiles[0].path}}`获取匹配的第一个文件的路径
|
||||
- 可以通过`{{subinput:placeholder}}`的格式来自定义占位符
|
||||
- `{{pwd}} `获取失败时返回桌面路径
|
||||
|
||||
#### 输出
|
||||
|
||||
- 输出实时动态显示,无需等到命令执行结束
|
||||
- 插件高度根据输出内容自适应,带子输入框的情况下,输出自动滚动
|
||||
- 输出的文本或 html 结果可以按 ctrl+f 进行搜索
|
||||
|
||||
|
||||
#### 平台
|
||||
|
||||
- 支持设置命令适配的平台
|
||||
|
||||
#### 运行
|
||||
|
||||
- 可以在新建命令的界面直接运行当前代码进行测试
|
||||
|
||||
#### 编辑器
|
||||
|
||||
- 支持 js、py 代码格式化
|
||||
- 新增cmd、applescript 代码高亮(原先用的 shell 的)
|
||||
- 支持代码提示和特殊变量提示,其中 cmd 和 shell 支持提示和补全当前环境变量下的命令,js 和 py 支持智能提示,其他语言支持关键字提示
|
||||
- 支持 vscode 快捷键
|
||||
|
||||
#### 导入导出
|
||||
|
||||
- 支持将命令导出到剪贴板,以及通过剪贴板导入命令
|
||||
|
||||
#### 其他
|
||||
|
||||
- 防误操作
|
||||
- 删除命令时会再次确认
|
||||
- 删除、清空命令时,会将删除的命令复制到剪贴板,可以通过导入命令快速恢复
|
||||
- 数据库存储结构重构,解决某些情况下数据同步时产生的异常
|
||||
|
||||
## v1.6.1
|
||||
|
||||
- 现在执行快捷命令时,会现将uTools的高度设置为0,如果有输出,再展开
|
||||
- 模拟动作模式增加支持`require`
|
||||
- 匹配主窗口输入模式下,会对输入的格式做简单校验
|
||||
|
||||
## v1.6.0
|
||||
|
||||
- 修复php乱码
|
||||
- 现在可以自定义输出的编码方式,脚本里选择`custom`
|
||||
- 模拟操作模式的`+延时`合并到`+动作中`
|
||||
|
||||
## v1.5.9
|
||||
|
||||
- 修复不能导入命令的bug
|
||||
- 由于uTools 1.0.0版本匹配窗口时无法再使用正则,所以选择匹配窗口模式时,在填写进程时需填写进程全名,多个进程逗号隔开
|
||||
- 忽略输出模式调整回自动隐藏窗口
|
||||
- 更新示例命令库
|
||||
|
||||
## v1.5.8
|
||||
|
||||
- 适配新版本
|
||||
|
||||
## v1.5.7
|
||||
|
||||
- 添加`模拟操作`的功能,在`类型`下拉框内选取
|
||||
- 该模式提供了模拟按键、打开文件、打开网站、定位文件、执行命令等实用功能
|
||||
- 结合模拟按键和窗口匹配,可以实现针对不同的软件模拟不同的操作
|
||||
- 该模式适用于没有编程基础的用户,只需要通过下拉框选取想要的功能,就可以快速编写一个简单的命令
|
||||
- 该模式同样适用于本机没有任何语言环境的或环境变量失效的用户,不需要安装nodejs即可执行js代码
|
||||
- 虽然没有提供`特殊变量`的下拉框,但实际是支持的
|
||||
- 脚本报错时提供跳转至临时脚本目录的选项
|
||||
- 添加一个`下载命令`的按钮,可以跳转到[样例命令库]( https://github.com/fofolee/uTools-quickcommand/tree/master/CommandCollections ),如果你不知道怎么去编写一个目录,可以尝试下载导入进行参考
|
||||
- (貌似)修复了执行完命令后,再次呼出uTools会短暂显示命令关键词的bug,该bug可能是由先隐藏uTools再退出插件所引起
|
||||
|
||||

|
||||
|
||||
## v1.5.1
|
||||
- 修复macOS下新增命令界面排版错误的问题
|
||||
- 修复macOS下使用shell脚本且发送输出结果到活动窗口时结果不正确的问题
|
||||
|
||||
## v1.5.0
|
||||
|
||||
- 注意本次更新对多处代码进行了重写,如果原有命令在上一版运行正常,在这一版出现了一些问题,请重新编辑该命令,修改命令的模式。如原有命令中使用了{{input}}变量的,关键字会变成[object object],请重新编辑该命令,将命令调整为`主输入框正则匹配`,如果原有命令中使用了{{pwd}}等变量的,请重新编辑该命令,将命令调整为`通过uTools呼出前的窗口匹配`
|
||||
- 原先命令自定了图标的,如果图标显示异常,重新选择设置一次图标即可
|
||||
|
||||

|
||||
|
||||
### 功能更新
|
||||
|
||||
- 新增`通过uTools呼出前的窗口匹配`的模式,现在可以快速编写一个应用到当前活动窗口的脚本
|
||||
- 现在可以分别通过`关键字`,`主输入框正则匹配`,`活动窗口匹配`三种方式来调用自定义的脚本
|
||||
|
||||
|
||||
- 新增一个特殊变量`当前窗口信息`,输出`json`格式的窗口相关信息,将原先的特殊变量`用户名`改为`本机唯一ID`,方便针对不同电脑编写脚本,两个变量均通过官方API获取
|
||||
- 弃用原先模拟按键的方式,获取资源管理器路径、选中文件、浏览器地址均借助官方API
|
||||
- 新增`发送系统通知`的输出模式 By [imxiny](https://github.com/imxiny)
|
||||
- 新增`在终端显示`的输出模式, 用以解决脚本需要显示动态输出的问题, 如 curl 命令 [issue](https://github.com/fofolee/uTools-quickcommand/issues/3)
|
||||
- 上传一些命令至仓库的`CommandCollections`文件夹,用以作为编写命令的样例,同时大家可以提交PR丰富这个命令库 [CommandCollections](https://github.com/fofolee/uTools-quickcommand/tree/master/CommandCollections)
|
||||
|
||||
### BUG 修复
|
||||
|
||||
- 修复 php 无法输入 <? 问题,修复MacOS环境变量问题,修复脚本不能带参数问题 By [dofy](https://github.com/dofy)
|
||||
- 修复中文乱码问题
|
||||
- 修复当使用子输入框时,会多次触发脚本的问题 [issue](https://github.com/fofolee/uTools-quickcommand/issues/5)
|
||||
- 修复当使用多行批处理脚本时,执行会报错的问题,该问题源于之前插件保存的批处理脚本的换行符是`LF`, windows 无法识别 [issue](https://yuanliao.info/d/424/70)
|
||||
|
||||
### 用户体验
|
||||
|
||||
- 获取选中文本,以及输出方式为发送到活动窗口时不再覆盖用户剪贴板
|
||||
|
||||
### 催更
|
||||
|
||||
- 之前了解到官方将出自动化插件,所以也有较长时间没有更新,在此以更催更
|
||||
|
||||
## v1.1.0
|
||||
|
||||
- 新增支持获取子输入框变量,感谢@ghostbody
|
||||
- 支持自定义解释器的路径
|
||||
|
||||
## v1.0.0
|
||||
|
||||
- 上架 uTools 商店,去掉插件自带更新
|
||||
- 精简了插件大小
|
||||
- 可以自定义语言
|
||||
|
||||
## v0.0.2
|
||||
|
||||
- 修复uTools更新后进入插件空白的BUG
|
||||
- 添加Linux支持
|
||||
- 修复导入BUG
|
||||
- 修复说明为空时无法启用命令的BUG
|
||||
- 添加全部导出和全部删除的功能
|
@@ -1,195 +0,0 @@
|
||||
[TOC]
|
||||
|
||||
# 一、更新日志
|
||||
|
||||
[更新日志戳我](CHANGELOG.html)
|
||||
|
||||
|
||||
|
||||
# 二、添加命令
|
||||
|
||||
## 基础
|
||||
|
||||
#### 常用动作
|
||||
|
||||
通过点击`+动作`<sup>①</sup>按钮进行添加
|
||||
|
||||
- 打开文件/文件夹/软件<sup>②</sup> (实现在主输入框启动自定义的软件名称及路径 )
|
||||
- 在文件管理器中定位文件
|
||||
- 用默认浏览器打开网址(实现类似网页快开的功能)
|
||||
- 用 ubrowser 打开网址
|
||||
- 执行系统命令
|
||||
- 将内容写入剪贴板
|
||||
- 发送系统消息
|
||||
- 弹窗显示消息
|
||||
- 发送文本到活动窗口
|
||||
- 转至指定插件(实现自定义插件关键字)
|
||||
- 添加延时
|
||||
|
||||
#### 模拟按键
|
||||
|
||||
通过点击`+按键`<sup>③</sup>按钮进行添加
|
||||
|
||||

|
||||
|
||||
## 进阶
|
||||
|
||||
#### 匹配
|
||||
|
||||
决定通过何种方式进入插件,不同的匹配模式也会影响插值变量的使用
|
||||
|
||||
##### 关键字
|
||||
|
||||
- 在主输入框输入对应关键字进入插件,最通用的一种模式,关键字可以设置多个
|
||||
|
||||
##### 正则/划词
|
||||
|
||||
- 正则匹配主输入框文本或唤出超级面板时选中的文本,可以获取输入框文本或选中文本作为变量
|
||||
|
||||
##### 窗口/进程
|
||||
|
||||
- 匹配呼出 uTools 前或唤出超级面板时的活动窗口,可以获取窗口的信息或文件夹路径作为变量
|
||||
|
||||
##### 复制/选中文件
|
||||
|
||||
- 匹配拖入主输入框的文件或唤出超级面板时选中的文件,可以获取复制及选中的文件信息作为变量
|
||||
|
||||
##### 专业模式
|
||||
- 匹配 JSON 格式的配置,等效于插件开发中的`features.cmds`
|
||||
|
||||

|
||||
|
||||
#### 环境
|
||||
|
||||
##### quickcommand
|
||||
|
||||
- 可以快速执行打开网址、软件、文件夹、模拟按键等高频动作的命令
|
||||
- 可以通过 quickcommand 的 api 来编写具有 UI 交互的脚本,详见[文档](./quickcommand.html)
|
||||
- 可以使用nodejs、electron、uTools 的 api, 其中 os、fs、path、child_process、util、axios、electron 已经预先 require 了, 无需再次 require ,详见[文档中的上下文一览](./quickcommand.html#上下文一览)
|
||||
|
||||
##### python、cmd、shell 、php 等环境
|
||||
|
||||
- 本机装了相应环境即可执行相应的脚本
|
||||
- 可以通过插值变量增强脚本的功能
|
||||
- 支持 10+ 语言
|
||||
- 可以通过 custom 手动设置解释器路径、参数、脚本后缀及编码方式
|
||||
|
||||

|
||||
|
||||
#### 插值变量
|
||||
|
||||
本插件内置了一些特殊的插值变量,可以获取一些特殊的值,能够加入到插件里的任意脚本中
|
||||
|
||||
##### 全模式可用
|
||||
|
||||
- `{{isWin}}` 是否Window系统, 返回1或0
|
||||
- `{{LocalId}}`本机唯一ID
|
||||
- `{{BrowserUrl}}` 浏览器的当前链接
|
||||
- `{{ClipText}}` 获取剪贴板的文本
|
||||
- `{{SelectText}}` 获取选中的文本 (已弃用)
|
||||
- `{{subinput}}`获取子输入框的文本,具有此变量时会在进入插件时自动启动子输入框
|
||||
- 可以通过`{{subinput:placeholder}}`的格式来自定义占位符
|
||||
|
||||
##### 匹配窗口/进程时可用
|
||||
|
||||
- `{{pwd}}` 资源管理器或访达的当前目录
|
||||
- `{{SelectFile}}` 文件管理器选中的文件,不支持 Linux
|
||||
- `{{WindowInfo}}`当前窗口信息,返回 JSON 格式字符串
|
||||
- 可以使用类似 `{{WindowInfo.id}}`的格式来直接读取相应的值
|
||||
|
||||
##### 匹配正则/划词时可用
|
||||
|
||||
- `{{input}} ` 获取主输入框的文本
|
||||
|
||||
##### 匹配复制/选中文件时可用
|
||||
|
||||
- `{{MatchedFiles}}` 匹配的文件,返回 JSON 格式字符串
|
||||
- 可以使用类似`{{MatchedFiles[0].path}}`的格式来直接读取相应的值
|
||||
|
||||

|
||||
|
||||
#### 输出
|
||||
|
||||
如果脚本有输出,则可以对输出内容做如下处理
|
||||
|
||||
- 隐藏并忽略输出
|
||||
- 显示纯文本输出 (不解析 html 内容)
|
||||
- 显示html格式的输出 (可以进一步编写简单的 GUI 界面,参考内置动作`特殊符号大全`)
|
||||
- 复制到剪贴板
|
||||
- 发送到活动窗口(可实现发送常用短语之类的功能)
|
||||
- 发送到系统通知
|
||||
- 在终端中显示
|
||||
|
||||

|
||||
|
||||
# 三、导出/分享/导入
|
||||
|
||||
#### 导出命令
|
||||
|
||||
点击命令旁的蓝色小箭头<sup>①</sup>即可导出命令,支持
|
||||
|
||||
- 导出到剪贴板<sup>②</sup>
|
||||
- 导出到文件<sup>③</sup>
|
||||
|
||||

|
||||
|
||||
#### 分享命令
|
||||
|
||||
点击分享命令即可一键分享当前的命令,初次分享命令,需要按照以下步骤设置 token:
|
||||
|
||||
1.通过 [https://www.yuque.com/g/fofolee/qcshares/collaborator/join?token=6LZn2vc34dqfIQdC]( https://www.yuque.com/g/fofolee/qcshares/collaborator/join?token=6LZn2vc34dqfIQdC ) 成为知识库成员,如果没有语雀账号,需要先注册一个
|
||||
|
||||

|
||||
|
||||
2.生成一个具有编辑权限的 token
|
||||
|
||||

|
||||
|
||||
3.点击命令旁的蓝色小箭头<sup>①</sup>,填入生成的 token <sup>②</sup> ,之后就可以尽情地分享命令啦
|
||||
|
||||

|
||||
|
||||
**注意:**
|
||||
|
||||
1. 命令的分享基于语雀共享知识库实现,语雀的共享知识库对知识库的成员没有太大约束,用官方的话来说,是` 基于信任和一起共建的基础上 `的,目前加入成为成员不需要通过审批确认,后期如果出现捣乱的情况会踢出成员并开启加入审批。先在此约定:**命令的分享请通过插件实现,不要在 web 端(即语雀知识库内)直接修改编辑分享的命令,否则可能会导致一些不可预知的错误**。
|
||||
|
||||
2. 同时为了保证命令的安全和质量,分享后的命令必须经过快捷命令插件作者本人`发布`后才能出现在`分享中心`当中。所有已发布的命令在[ https://www.yuque.com/fofolee/qcreleases ]( https://www.yuque.com/fofolee/qcreleases )可以查看到。
|
||||
|
||||
#### 导入命令
|
||||
|
||||
- 可以通过点击底部的`导入命令`来导入命令
|
||||
- 会优先识别剪贴板,如果剪贴板内有正确格式的命令会自动导入
|
||||
- 如果剪贴板内没有,则会弹出文件选择框
|
||||
- 支持自动识别是单个导出的命令还是全部导出的命令
|
||||
|
||||
#### 获取分享
|
||||
|
||||
可以通过以下两种方式来获取分享:
|
||||
|
||||
1. 点击设置界面底部的`分享中心`<sup>①</sup>即可获取并导入在线分享的命令
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
2. 访问[ https://www.yuque.com/fofolee/qcreleases ]( https://www.yuque.com/fofolee/qcreleases )即可查看发布的命令
|
||||
|
||||
# 四、关于
|
||||
|
||||
#### 作者
|
||||
|
||||
<img width="30px" src="https://s1.ax1x.com/2020/07/14/UaDkdg.png">[github @fofolee]( https://github.com/fofolee )
|
||||
|
||||
<img width="30px" src=" https://yuanliao.info/assets/avatars/frbg0owd6t3mmejs.png ">[猿料社区 @fofolee]( https://yuanliao.info/u/fofolee/discussions )
|
||||
|
||||
#### 赞赏码
|
||||
|
||||
鉴于之前某位可爱的同学捐赠的时候捐错了对象,此处献上我的捐赠码~
|
||||
|
||||
<img width="500px" src="https://s1.ax1x.com/2020/07/15/Uacgqx.png">
|
||||
|
||||
#### 所有插件
|
||||
|
||||
所有由我制作的插件如下表所示,有兴趣的童鞋可以试一试~
|
||||
|
||||
<table> <tbody> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/c6e808470b6cbc778865e9ed1bebf339.png"></td> <td>快捷命令</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/11bf712cc79499549754586fff7c8db1.png"></td> <td>程序员手册</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/dd0977b7d74db32d7088795ef62a7769.png"></td> <td>bilibili</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/f26a31d11af3a54f9bddd7e781da46d5.png"></td> <td>关闭进程</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/80eae148109a4d7001232efebdd14aca.png"></td> <td>插件面板</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/223f27b647b184ffdb2cd9f05a99d50a.png"></td> <td>随机壁纸</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/480ac04e8ea522b7bb0dae6418e177a4.png"></td> <td>Github助手</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/0de374539c3c358d122ca652d26b5b6e.png"></td> <td>文件夹助手</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/5f9fd2f37445a462c0735b9dcca828cd.png"></td> <td>emoji搜索</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/50ef534638a9fc7fbed5274131afe503.png"></td> <td>😩能不能好好说话</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/9863ccc91f32b4ab660d6e58dd8b04ae.png"></td> <td>winget</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/6abb03b743259bd4c976d2a29da0a395.png"></td> <td>icons8搜索</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/527a20566499e7c3fb63e8705d60ccb7.png"></td> <td>kali 工具介绍</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> <tr> <td><img style="max-width:30px;max-height:30px" src="https://res.u-tools.cn/plugins/logo/3ef0e794b7950193fc98289ea2b199e9.png"></td> <td>png转icon</td> <td><button onclick="utools.redirect(this.parentNode.parentNode.querySelector('td+td').innerText)">戳我</button></td> </tr> </tbody> </table>
|
@@ -1,477 +0,0 @@
|
||||
[TOC]
|
||||
|
||||
## quickcommand
|
||||
|
||||
### ❖ UI 交互
|
||||
|
||||
#### `showButtonBox(buttons, title)`
|
||||
|
||||
- buttons: Array 每一个元素对应一个按钮
|
||||
- title: String | undefined 对话框标题
|
||||
- 返回: Promise
|
||||
- id: Integer 按钮的序号,从0开始
|
||||
- text: String 按钮的文本
|
||||
|
||||
显示一个按钮对话框,用来接收用户的输入
|
||||
|
||||
**示例**
|
||||
|
||||
```js
|
||||
// then 写法
|
||||
quickcommand.showButtonBox(["按钮1", "按钮2", "按钮3"]).then(({ id, text }) => {
|
||||
console.log(`选择了第${id+1}个按钮`)
|
||||
console.log(`按钮的文本为${text}`)
|
||||
})
|
||||
|
||||
// async 写法
|
||||
(async () =>{
|
||||
let button = await quickcommand.showButtonBox(["按钮1", "按钮2", "按钮3"])
|
||||
console.log(`选择了第${button.id+1}个按钮`)
|
||||
console.log(`按钮的文本为${button.text}`)
|
||||
})()
|
||||
|
||||
// 捕获错误
|
||||
quickcommand.showButtonBox().catch(e => {
|
||||
console.log(e)
|
||||
})
|
||||
```
|
||||
**实例**
|
||||
|
||||
```js
|
||||
// 截取自内置快捷命令: 通过 find 查找文件
|
||||
quickcommand.showButtonBox(['打开文件', '在文件管理器中定位', '复制文件路径']).then(x => {
|
||||
switch (x.id) {
|
||||
case 0:
|
||||
utools.shellOpenItem(file);
|
||||
break;
|
||||
case 1:
|
||||
utools.shellShowItemInFolder(file);
|
||||
break;
|
||||
case 2:
|
||||
utools.copyText(file);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### `showInputBox(placeHolders, title)`
|
||||
|
||||
- placeHolders: Array 每一个占位符对应一个输入框
|
||||
- title: String | undefined 对话框标题
|
||||
- 返回: Promise
|
||||
- values: Array 所以输入框的值
|
||||
|
||||
显示一个输入框界面,用来接用户的输入
|
||||
|
||||
**示例**
|
||||
|
||||
```js
|
||||
quickcommand.showInputBox(["输入框1", "输入框2", "输入框3"]).then(values => {
|
||||
console.log(`输入的内容分别为${values}`)
|
||||
})
|
||||
```
|
||||
**实例**
|
||||
|
||||
```js
|
||||
// 截取自内置快捷命令: 文本替换
|
||||
quickcommand.showInputBox(["要替换的内容,两边加 / 使用正则", "替换为的内容"]).then(inputs => {
|
||||
var search = inputs[0]
|
||||
var repl = inputs[1]
|
||||
...
|
||||
utools.hideMainWindow()
|
||||
quickcommand.sleep(300)
|
||||
quickcommand.simulateCopy()
|
||||
quickcommand.sleep(100)
|
||||
var source = electron.clipboard.readText()
|
||||
source = source.replace(search, repl)
|
||||
...
|
||||
})
|
||||
```
|
||||
#### `showSelectList(selects, options)`
|
||||
|
||||
- selects: Array 每一个元素对应一个列表选项
|
||||
- options: Object | undefined 列表的选项
|
||||
- placeholder: String 搜索框占位符
|
||||
- optionType: String 选项的格式,有`plaintext`、`html`、`json`三种,默认为`plaintext`
|
||||
- 返回: Promise
|
||||
- id: Integer 选择的序号,从0开始
|
||||
- text: String 选择的文本
|
||||
- title/description/[…]: 当`optionType`为`json`时,对应`json`里的每一个属性
|
||||
|
||||
显示一个支持搜索的且可以动态更新的选项列表
|
||||
|
||||
当指定`optionType`为`json`时,类似于插件开发的`列表模式`,`title`、`description`和`icon`分别表示标题、描述和图标,其中`title`为必备属性
|
||||
|
||||
**示例**
|
||||
|
||||
```js
|
||||
// plaintext
|
||||
var opt = []
|
||||
for (var i = 0; i < 15; i++) {
|
||||
// 每一个选项为文本格式
|
||||
opt.push(`选项` + i)
|
||||
}
|
||||
quickcommand.showSelectList(opt).then(choise => {
|
||||
console.log(`选择的选项为${choise.text}`)
|
||||
})
|
||||
|
||||
// json
|
||||
var opt = []
|
||||
for (var i = 0; i < 15; i++) {
|
||||
// 每一个选项为 json 格式
|
||||
opt.push({title: `选项${i}`, description: `选项${i}的描述`, abcd: `选项${i}的自定义属性`})
|
||||
}
|
||||
quickcommand.showSelectList(opt, {optionType: 'json'}).then(choise => {
|
||||
console.log(`选择的选项为${choise.title}`)
|
||||
})
|
||||
|
||||
// html
|
||||
var opt = []
|
||||
for (var i = 0; i < 15; i++) {
|
||||
// 每一个选项为 html 格式
|
||||
opt.push(`<div style="color: red">选项${i}</div>`)
|
||||
}
|
||||
quickcommand.showSelectList(opt, {optionType: 'html'}).then(choise => {
|
||||
console.log(`选择的选项为${quickcommand.htmlParse(choise.text).body.innerText}`)
|
||||
})
|
||||
```
|
||||
**实例**
|
||||
|
||||
```js
|
||||
// 截取自内置快捷命令: 文本处理
|
||||
let textManipulation = [ ... ]
|
||||
let text = quickcommand.payload
|
||||
let options = textManipulation.map(t => {
|
||||
return {
|
||||
title: t.name,
|
||||
description: t.func(text)
|
||||
}
|
||||
})
|
||||
|
||||
quickcommand.showSelectList(options, { optionType: 'json' })
|
||||
.then(choise => {
|
||||
console.log(choise.description)
|
||||
utools.copyText(choise.description)
|
||||
})
|
||||
|
||||
axios.post('http://fy.iciba.com/ajax.php?a=fy', `f=auto&t=auto&w=${text}`)
|
||||
.then(res => {
|
||||
let content = res.data.content
|
||||
let trans = content.out ? content.out : content.word_mean
|
||||
let opt = textManipulation[0]
|
||||
opt.description = trans
|
||||
quickcommand.updateSelectList(opt, 0)
|
||||
})
|
||||
```
|
||||
|
||||
#### `updateSelectList(opt, id)`
|
||||
|
||||
- opt: String 要更新的选项
|
||||
- id: Integer | undefined: 要更新的选项的序号,不赋值时则追加到最后一个选项后面
|
||||
|
||||
动态更新当前的选项列表的选项。
|
||||
|
||||
**示例**
|
||||
|
||||
```js
|
||||
// 初始状态只有 1、2、3 三个选项
|
||||
quickcommand.showSelectList(['1','2','3']).then(x=>{
|
||||
console.log(x)
|
||||
})
|
||||
|
||||
// 1s 后追加一个选项
|
||||
quickcommand.setTimeout(()=>{
|
||||
quickcommand.updateSelectList('4')
|
||||
}, 1000)
|
||||
|
||||
// 2s 后更新第二个选项的值
|
||||
quickcommand.setTimeout(()=>{
|
||||
quickcommand.updateSelectList('updated', 1)
|
||||
}, 2000)
|
||||
```
|
||||
|
||||
#### `showTextAera(placeholder, value)`
|
||||
|
||||
- placeholder: String | undefined 文本框占位符
|
||||
- value: String | undefined 默认的文本值
|
||||
- 返回: Promise
|
||||
- text: String 文本框的文本
|
||||
|
||||
显示一个文本框界面,用来接用户的输入
|
||||
|
||||
**示例**
|
||||
|
||||
```js
|
||||
quickcommand.showTextAera("请输入文本").then(text=>{
|
||||
console.log(`输入的文本为${text}`)
|
||||
})
|
||||
```
|
||||
|
||||
**实例**
|
||||
|
||||
```js
|
||||
// 截取自内置快捷命令: vscode代码片段生成器
|
||||
var snippet = {}
|
||||
quickcommand.showTextAera("请输入代码片段").then(code => {
|
||||
snippet.body = code.split("\n")
|
||||
quickcommand.showInputBox(["代码片段的描述", "触发代码片段的关键词"])
|
||||
.then(inputs => {
|
||||
snippet.prefix = inputs[1]
|
||||
snippet.description = inputs[0]
|
||||
var result = `"${inputs[0]}": ` + JSON.stringify(snippet, null, '\t')
|
||||
console.log(result)
|
||||
utools.copyText(result)
|
||||
quickcommand.showMessageBox('已复制')
|
||||
})
|
||||
})
|
||||
```
|
||||
#### `showMessageBox(message, icon, time)`
|
||||
|
||||
- message: String 显示的消息内容
|
||||
- icon: String | undefined 图标,可为`success`、`error`、`warning`、`info`、`question`,默认为`success`
|
||||
- time: Integer | undefined 多少毫秒后消失,默认为`3000`
|
||||
|
||||
显示一个自动消失的提示框
|
||||
|
||||
**示例**
|
||||
|
||||
```js
|
||||
quickcommand.showMessageBox("这是一段3s后自动消失的成功提示")
|
||||
quickcommand.showMessageBox("这是一段3s后自动消失的失败提示", "error")
|
||||
```
|
||||
|
||||
#### `showConfirmBox(title)`
|
||||
|
||||
- title: String | undefined 提示的标题
|
||||
- 返回: Promise
|
||||
- confirmed: Boolean | undefined 是否点击了确定按钮
|
||||
|
||||
显示一个确认框
|
||||
|
||||
```js
|
||||
quickcommand.showConfirmBox().then(confirmed => {
|
||||
confirmed && console.log('点击了确定')
|
||||
})
|
||||
```
|
||||
|
||||
### ❖ 延时函数
|
||||
|
||||
#### `sleep(ms)`
|
||||
|
||||
- ms: Integer 等待的毫秒
|
||||
|
||||
由于`setTimeout`在electron中存在限制,在隐藏到后台时不会被执行,在vm2中也有bug,所以在quickcommand的环境下被禁用了,但对于模拟按键之类的场景,延迟是不可缺少的,所以提供了`sleep`函数来解决这个问题
|
||||
|
||||
**示例**
|
||||
|
||||
```js
|
||||
utools.simulateKeyboardTap('d', 'alt')
|
||||
quickcommand.sleep(200)
|
||||
utools.simulateKeyboardTap('c', 'ctrl')
|
||||
```
|
||||
|
||||
#### `setTimeout(callback, ms)`
|
||||
|
||||
- callback: Function 回调函数
|
||||
- ms: Integer 延时的毫秒
|
||||
|
||||
用法和`setTimeout`一样,但实现原理不一样,`sleep`的异步版本
|
||||
|
||||
**示例**
|
||||
|
||||
```js
|
||||
quickcommand.setTimeout(()=>{
|
||||
console.log('2000毫秒后执行')
|
||||
}, 2000)
|
||||
```
|
||||
### ❖ 前端封装
|
||||
|
||||
#### `htmlParse(html)`
|
||||
|
||||
- html: String 需要解析的`html`文本
|
||||
- 返回: Object `DOM`对象
|
||||
|
||||
将给定的`html`字符串解析为`DOM`对象,用于快速编写爬虫脚本
|
||||
|
||||
**示例**
|
||||
|
||||
```js
|
||||
var html = `<a href="https://u.tools/">uTools</a>`
|
||||
var href = quickcommand.htmlParse(html).querySelector('a').href
|
||||
console.log(`解析出来的a标签地址为${href}`)
|
||||
```
|
||||
|
||||
#### `downloadFile(url, file | options)`
|
||||
|
||||
- url: String 地址
|
||||
- file | options :
|
||||
- file: String 当赋值为文件路径时,则表示下载文件的绝对路径
|
||||
- options: Object | undefined 不赋值时,则会弹出对话框要求选择下载到的路径, 赋值为 `Object `时,表示弹出对话框的 `options `,格式和 `utools.showSaveDialog` 中的 `options `一致
|
||||
- 返回: Promise
|
||||
- content: Buffer 文件的内容
|
||||
|
||||
下载文件,可选直接下载到指定路径,或者弹出对话框选择下载路径
|
||||
|
||||
```js
|
||||
// 下载文件到D:/
|
||||
quickcommand.downloadFile('https://res.u-tools.cn/currentversion/uTools-1.1.3.exe', 'D:/')
|
||||
|
||||
// 下载文件,并弹出对话框询问保存路径
|
||||
quickcommand.downloadFile('https://res.u-tools.cn/currentversion/uTools-1.1.3.exe')
|
||||
```
|
||||
|
||||
#### `uploadFile(url, file | options, name, formData)`
|
||||
|
||||
- url: String 地址
|
||||
- file | options :
|
||||
- file: String 当赋值为文件路径时,则表示要上传的文件的绝对路径
|
||||
- options: Object | undefined 不赋值时,则会弹出对话框要求选择要上传的文件的路径, 赋值为 `Object `时,表示弹出对话框的 `options `,格式和 `utools.showOpenDialog` 中的 `options `一致
|
||||
- name: String | undefined 文件名,默认为`file`
|
||||
- formData: Object | undefined 其他需要添加的表单数据
|
||||
- 返回: Promise
|
||||
- response: Object 响应内容
|
||||
|
||||
上传文件,可以直接上传指定文件,或者弹出对话框选择要上传的文件,可以自定义表单数据
|
||||
|
||||
```js
|
||||
// 上传图片到图床
|
||||
quickcommand.uploadFile("https://imgkr.com/api/v2/files/upload", "C:\\test.jpg").then(res=>{
|
||||
console.log('上传成功,图片地址为:' + res.data.data)
|
||||
})
|
||||
|
||||
// 包含额外表单数据
|
||||
quickcommand.uploadFile("https://catbox.moe/user/api.php", "C:\\test.jpg", 'fileToUpload', {
|
||||
"reqtype": "fileupload"
|
||||
}).then(res=>{
|
||||
console.log('上传成功,图片地址为:' + res.data)
|
||||
})
|
||||
```
|
||||
|
||||
### ❖ nodejs 封装
|
||||
|
||||
#### `loadRemoteScript(url)`
|
||||
|
||||
- url: String 脚本地址
|
||||
|
||||
- 返回: Promise
|
||||
- Object: Object 返回从远程脚本加载的对象
|
||||
|
||||
加载一个远程脚本文件
|
||||
|
||||
```js
|
||||
let remote = 'https://cdn.jsdelivr.net/npm/sweetalert2@9'
|
||||
quickcommand.loadRemoteScript(remote).then(swal => {
|
||||
swal.fire('已加载 sweetalert2 并成功弹窗')
|
||||
})
|
||||
|
||||
// async/await
|
||||
(async () => {
|
||||
let remote = 'https://cdn.jsdelivr.net/npm/sweetalert2@9'
|
||||
const swal = await quickcommand.loadRemoteScript(remote)
|
||||
swal.fire('已加载 sweetalert2 并成功弹窗')
|
||||
})()
|
||||
```
|
||||
|
||||
#### `kill(pid, signal)`
|
||||
|
||||
- pid: Integer 进程 ID
|
||||
- signal: String | Integer | undefined 将发送的信号,默认为 `'SIGTERM'`
|
||||
|
||||
将 `signal` 发送给 `pid` 标识的进程 , 默认为关闭进程,同`process.kill`
|
||||
|
||||
**示例**
|
||||
|
||||
```js
|
||||
quickcommand.kill(16084)
|
||||
```
|
||||
|
||||
#### `runVbs(script)`
|
||||
- script: String VBS代码
|
||||
- 返回: Promise
|
||||
- Result: String 脚本运行结果
|
||||
|
||||
windows 下运行 VBS 脚本
|
||||
|
||||
**示例**
|
||||
|
||||
```js
|
||||
quickcommand.runVbs(`CreateObject("SAPI.SpVoice").Speak"Hello"`)
|
||||
```
|
||||
|
||||
### ❖ utools 封装
|
||||
|
||||
#### `enterData`
|
||||
|
||||
- Object 对应`utools.onPluginEnter` 的 `code` `type` 和 `payload`
|
||||
- code: String 唯一标示
|
||||
- type: 匹配模式,可以为 `text` `img` `files` `regex` `over` `window`
|
||||
- payload: 当匹配模式为`关键字`时,返回进入插件的关键字;为`正则`时,返回匹配的文本;为`窗口`时,返回匹配的窗口信息;为`文件`时,返回匹配的文件信息
|
||||
|
||||
**示例**
|
||||
|
||||
```js
|
||||
// 匹配模式为正则/划词时
|
||||
if (quickcommand.enterData.type == 'regex'){
|
||||
var text = quickcommand.enterData.payload
|
||||
console.log(`主输入框匹配的文本为${text}`)
|
||||
}
|
||||
```
|
||||
|
||||
#### `simulateCopy()`
|
||||
|
||||
模拟复制操作
|
||||
|
||||
#### `simulatePaste()`
|
||||
|
||||
模拟粘贴操作
|
||||
|
||||
## 其他
|
||||
|
||||
### nodejs
|
||||
|
||||
#### ❖ <a href ="javascript:utools.ubrowser.goto('http://nodejs.cn/api/').run({width: 1280, height: 920})">文档</a>
|
||||
|
||||
#### ❖ 上下文
|
||||
|
||||
- **require**: *ƒ require(path)*
|
||||
- **os**: {arch: *ƒ*, cpus: *ƒ*, endianness: *ƒ*, freemem: *ƒ*, getPriority: *ƒ*, …}
|
||||
- **fs**: {appendFile: *ƒ*, appendFileSync: *ƒ*, access: *ƒ*, accessSync: *ƒ*, chown: *ƒ*, …}
|
||||
- **path**: {resolve: *ƒ*, normalize: *ƒ*, isAbsolute: *ƒ*, join: *ƒ*, relative: *ƒ*, …}
|
||||
- **child_process**: {_forkChild: *ƒ*, ChildProcess: *ƒ*, exec: *ƒ*, execFile: *ƒ*, execFileSync: *ƒ*, …}
|
||||
- **util**: {_errnoException: *ƒ*, _exceptionWithHostPort: *ƒ*, _extend: *ƒ*, callbackify: *ƒ*, debuglog: *ƒ*, …}
|
||||
- **Buffer**: *ƒ Buffer(arg, encodingOrOffset, length)*
|
||||
- **process**: process {version: "v12.14.1", versions: {…}, arch: "x64", …}
|
||||
- **TextDecoder**: *ƒ TextDecoder()*
|
||||
- **TextEncoder**: *ƒ TextEncoder()*
|
||||
- **URL**: *ƒ URL()*
|
||||
- **URLSearchParams**: *ƒ URLSearchParams()*
|
||||
- **axios**: *ƒ* *wrap()*
|
||||
- [文档](./axios.html)
|
||||
|
||||
### electron
|
||||
|
||||
#### ❖ <a href ="javascript:utools.ubrowser.goto('http://www.electronjs.org/docs').run({width: 1280, height: 920})">文档</a>
|
||||
|
||||
#### ❖ 上下文
|
||||
|
||||
- **clipboard**: Object
|
||||
- **contextBridge**: Object
|
||||
- **crashReporter**: Object
|
||||
- **desktopCapturer**: Object
|
||||
- **ipcRenderer**: EventEmitter
|
||||
- **nativeImage**: Object
|
||||
- **shell**: Object
|
||||
- **webFrame**: WebFrame
|
||||
|
||||
### utools
|
||||
|
||||
#### ❖ <a href ="javascript:utools.ubrowser.goto('https://u.tools/docs/developer/api.html').run({width: 1280, height: 920})">文档</a>
|
||||
|
||||
#### ❖ 上下文
|
||||
|
||||
- all except below
|
||||
- ~~db~~
|
||||
- ~~removeFeature~~
|
||||
- ~~setFeature~~
|
@@ -1,132 +0,0 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||
typeof define === 'function' && define.amd ? define(factory) :
|
||||
(global = global || self, global.pictureCompress = factory());
|
||||
}(this, function () { 'use strict';
|
||||
|
||||
/**
|
||||
* 将图片压缩为对应尺寸
|
||||
* @param {Object} options
|
||||
* @param {String} options.img 图片的url或者base64数据
|
||||
* @param {Number} options.width 目标图片的宽度
|
||||
* @param {Number} options.height 目标图片的高度
|
||||
* @param {Number} options.quality 生成目标图片质量
|
||||
* @param {String} options.fit 图片压缩填充模式默认 scale:按比例缩放,可选 fill:按使用目标尺寸
|
||||
* @param {String} options.type 图片压缩类型默认 jpg,可选 png
|
||||
* @param {Number} options.rotate 图片旋转,由于手机拍照的角度和我们使用的头像不一致,需要旋转 默认0 仅支持 90 180 -90
|
||||
* @returns {Promise} then {width,height,img}
|
||||
*/
|
||||
function pictureCompress(options) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
if (!options.img) {
|
||||
reject(new Error('need img'));
|
||||
return;
|
||||
}
|
||||
|
||||
var imgSrc = options.img,
|
||||
width = options.width || 640,
|
||||
height = options.height || 640,
|
||||
type = options.type || 'jpg',
|
||||
quality = options.quality || 0.92,
|
||||
fit = options.fit || 'scale',
|
||||
rotate = options.rotate || 0;
|
||||
|
||||
if (width <= 0 || height <= 0) {
|
||||
reject(new Error('dist width or height need > 0'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!/jpg|png|jpeg/.test(type)) {
|
||||
reject(new Error('type need jpg or png!'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (rotate !== 90 && rotate !== -90 && rotate !== 0 && rotate !== 180) {
|
||||
reject(new Error('rotate mast be 0 90 -90 180!'));
|
||||
return;
|
||||
}
|
||||
|
||||
var changeWidthAndHeight = rotate === 90 || rotate === -90;
|
||||
var image = new Image();
|
||||
image.src = imgSrc;
|
||||
|
||||
image.onload = function () {
|
||||
var distSize = getDistSize({
|
||||
width: changeWidthAndHeight ? this.naturalHeight : this.naturalWidth,
|
||||
height: changeWidthAndHeight ? this.naturalWidth : this.naturalHeight
|
||||
}, {
|
||||
width: changeWidthAndHeight ? height : width,
|
||||
height: changeWidthAndHeight ? width : height
|
||||
}, fit);
|
||||
var imgData = compress(this, distSize.width, distSize.height, type, quality, rotate);
|
||||
resolve({
|
||||
width: distSize.width,
|
||||
height: distSize.height,
|
||||
img: imgData
|
||||
});
|
||||
};
|
||||
|
||||
image.onerror = function (err) {
|
||||
reject(err);
|
||||
};
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 将图片转换为固定尺寸的
|
||||
* @param {Image} img 图片数据
|
||||
* @param {Number} width 转换之后的图片宽度
|
||||
* @param {Number} height 转换之后的图片高度
|
||||
* @param {String} type base64的图片类型 jpg png
|
||||
* @param {Number} quality 转换之后的图片质量
|
||||
*/
|
||||
|
||||
|
||||
function compress(img, width, height, type, quality, rotate) {
|
||||
var canvas = document.createElement('canvas');
|
||||
var ctx = canvas.getContext('2d');
|
||||
var types = {
|
||||
'jpg': 'image/jpeg',
|
||||
'jpeg': 'image/jpeg',
|
||||
'png': 'image/png'
|
||||
};
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
if (rotate === 90) {
|
||||
ctx.translate(width, 0);
|
||||
ctx.rotate(90 * Math.PI / 180);
|
||||
ctx.drawImage(img, 0, 0, height, width);
|
||||
} else if (rotate === -90) {
|
||||
ctx.translate(0, height);
|
||||
ctx.rotate(-90 * Math.PI / 180);
|
||||
ctx.drawImage(img, 0, 0, height, width);
|
||||
} else if (rotate === 180) {
|
||||
ctx.translate(width, height);
|
||||
ctx.rotate(180 * Math.PI / 180);
|
||||
ctx.drawImage(img, 0, 0, width, height);
|
||||
} else {
|
||||
ctx.drawImage(img, 0, 0, width, height);
|
||||
}
|
||||
|
||||
return canvas.toDataURL(types[type], quality);
|
||||
}
|
||||
/**
|
||||
* 选择源尺寸与目标尺寸比例中较小的那个,保证图片可以完全显示
|
||||
* 最大值不超过1,如果图片源尺寸小于目标尺寸,则不做处理,返回图片原尺寸
|
||||
* @param {Object} source 源图片的宽高
|
||||
* @param {Object} dist 目标图片的宽高
|
||||
*/
|
||||
|
||||
|
||||
function getDistSize(source, dist, fit) {
|
||||
if (fit === 'fill') return dist;
|
||||
var scale = Math.min(dist.width / source.width, dist.height / source.height, 1);
|
||||
return {
|
||||
width: Math.round(source.width * scale),
|
||||
height: Math.round(source.height * scale)
|
||||
};
|
||||
}
|
||||
|
||||
return pictureCompress;
|
||||
|
||||
}));
|
@@ -1,3 +0,0 @@
|
||||
if (parseInt(process.versions.node.split('.')[0]) < 6) throw new Error('vm2 requires Node.js version 6 or newer.');
|
||||
|
||||
module.exports = require('./lib/main');
|
@@ -1,35 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const pa = require('path');
|
||||
|
||||
const {NodeVM, VMError} = require('../');
|
||||
|
||||
if (process.argv[2]) {
|
||||
const path = pa.resolve(process.argv[2]);
|
||||
|
||||
console.log(`\x1B[90m[vm] creating VM for ${path}\x1B[39m`);
|
||||
const started = Date.now();
|
||||
|
||||
try {
|
||||
NodeVM.file(path, {
|
||||
verbose: true,
|
||||
require: {
|
||||
external: true
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`\x1B[90m[vm] VM completed in ${Date.now() - started}ms\x1B[39m`);
|
||||
} catch (ex) {
|
||||
if (ex instanceof VMError) {
|
||||
console.error(`\x1B[31m[vm:error] ${ex.message}\x1B[39m`);
|
||||
} else {
|
||||
const {stack} = ex;
|
||||
|
||||
if (stack) {
|
||||
console.error(`\x1B[31m[vm:error] ${stack}\x1B[39m`);
|
||||
} else {
|
||||
console.error(`\x1B[31m[vm:error] ${ex}\x1B[39m`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,83 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// eslint-disable-next-line no-invalid-this, no-shadow
|
||||
const {GeneratorFunction, AsyncFunction, AsyncGeneratorFunction, global, internal, host, hook} = this;
|
||||
const {Contextify, Decontextify} = internal;
|
||||
// eslint-disable-next-line no-shadow
|
||||
const {Function, eval: eval_, Promise, Object, Reflect} = global;
|
||||
const {getOwnPropertyDescriptor, defineProperty, assign} = Object;
|
||||
const {apply: rApply, construct: rConstruct} = Reflect;
|
||||
|
||||
const FunctionHandler = {
|
||||
__proto__: null,
|
||||
apply(target, thiz, args) {
|
||||
const type = this.type;
|
||||
args = Decontextify.arguments(args);
|
||||
try {
|
||||
args = Contextify.value(hook(type, args));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
return rApply(target, thiz, args);
|
||||
},
|
||||
construct(target, args, newTarget) {
|
||||
const type = this.type;
|
||||
args = Decontextify.arguments(args);
|
||||
try {
|
||||
args = Contextify.value(hook(type, args));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
return rConstruct(target, args, newTarget);
|
||||
}
|
||||
};
|
||||
|
||||
function makeCheckFunction(type) {
|
||||
return assign({
|
||||
__proto__: null,
|
||||
type
|
||||
}, FunctionHandler);
|
||||
}
|
||||
|
||||
function override(obj, prop, value) {
|
||||
const desc = getOwnPropertyDescriptor(obj, prop);
|
||||
desc.value = value;
|
||||
defineProperty(obj, prop, desc);
|
||||
}
|
||||
|
||||
const proxiedFunction = new host.Proxy(Function, makeCheckFunction('function'));
|
||||
override(Function.prototype, 'constructor', proxiedFunction);
|
||||
if (GeneratorFunction) {
|
||||
Object.setPrototypeOf(GeneratorFunction, proxiedFunction);
|
||||
override(GeneratorFunction.prototype, 'constructor', new host.Proxy(GeneratorFunction, makeCheckFunction('generator_function')));
|
||||
}
|
||||
if (AsyncFunction) {
|
||||
Object.setPrototypeOf(AsyncFunction, proxiedFunction);
|
||||
override(AsyncFunction.prototype, 'constructor', new host.Proxy(AsyncFunction, makeCheckFunction('async_function')));
|
||||
}
|
||||
if (AsyncGeneratorFunction) {
|
||||
Object.setPrototypeOf(AsyncGeneratorFunction, proxiedFunction);
|
||||
override(AsyncGeneratorFunction.prototype, 'constructor', new host.Proxy(AsyncGeneratorFunction, makeCheckFunction('async_generator_function')));
|
||||
}
|
||||
|
||||
global.Function = proxiedFunction;
|
||||
global.eval = new host.Proxy(eval_, makeCheckFunction('eval'));
|
||||
|
||||
if (Promise) {
|
||||
|
||||
Promise.prototype.then = new host.Proxy(Promise.prototype.then, makeCheckFunction('promise_then'));
|
||||
// This seems not to work, and will produce
|
||||
// UnhandledPromiseRejectionWarning: TypeError: Method Promise.prototype.then called on incompatible receiver [object Object].
|
||||
// This is likely caused since the host.Promise.prototype.then cannot use the VM Proxy object.
|
||||
// Contextify.connect(host.Promise.prototype.then, Promise.prototype.then);
|
||||
|
||||
if (Promise.prototype.finally) {
|
||||
Promise.prototype.finally = new host.Proxy(Promise.prototype.finally, makeCheckFunction('promise_finally'));
|
||||
// Contextify.connect(host.Promise.prototype.finally, Promise.prototype.finally);
|
||||
}
|
||||
if (Promise.prototype.catch) {
|
||||
Promise.prototype.catch = new host.Proxy(Promise.prototype.catch, makeCheckFunction('promise_catch'));
|
||||
// Contextify.connect(host.Promise.prototype.catch, Promise.prototype.catch);
|
||||
}
|
||||
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
// source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping
|
||||
function escapeRegExp(string) {
|
||||
return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
||||
}
|
||||
|
||||
function match(wildcard, s) {
|
||||
const regexString = escapeRegExp(wildcard).replace(/\\\*/g, '\\S*').replace(/\\\?/g, '.');
|
||||
const regex = new RegExp(regexString);
|
||||
return regex.test(s);
|
||||
}
|
||||
|
||||
module.exports = {match};
|
@@ -1,682 +0,0 @@
|
||||
/* eslint-disable no-shadow, no-invalid-this */
|
||||
/* global vm, host, Contextify, Decontextify, VMError, options */
|
||||
|
||||
'use strict';
|
||||
|
||||
const {Script} = host.require('vm');
|
||||
const fs = host.require('fs');
|
||||
const pa = host.require('path');
|
||||
|
||||
const BUILTIN_MODULES = host.process.binding('natives');
|
||||
const parseJSON = JSON.parse;
|
||||
const importModuleDynamically = () => {
|
||||
// We can't throw an error object here because since vm.Script doesn't store a context, we can't properly contextify that error object.
|
||||
// eslint-disable-next-line no-throw-literal
|
||||
throw 'Dynamic imports are not allowed.';
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Object} host Hosts's internal objects.
|
||||
*/
|
||||
|
||||
return ((vm, host) => {
|
||||
'use strict';
|
||||
|
||||
const global = this;
|
||||
|
||||
const TIMERS = new host.WeakMap(); // Contains map of timers created inside sandbox
|
||||
const BUILTINS = {__proto__: null};
|
||||
const CACHE = {__proto__: null};
|
||||
const EXTENSIONS = {
|
||||
__proto__: null,
|
||||
['.json'](module, filename) {
|
||||
try {
|
||||
const code = fs.readFileSync(filename, 'utf8');
|
||||
module.exports = parseJSON(code);
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
['.node'](module, filename) {
|
||||
if (vm.options.require.context === 'sandbox') throw new VMError('Native modules can be required only with context set to \'host\'.');
|
||||
|
||||
try {
|
||||
module.exports = Contextify.readonly(host.require(filename));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (let i = 0; i < vm.options.sourceExtensions.length; i++) {
|
||||
const ext = vm.options.sourceExtensions[i];
|
||||
|
||||
EXTENSIONS['.' + ext] = (module, filename, dirname) => {
|
||||
if (vm.options.require.context !== 'sandbox') {
|
||||
try {
|
||||
module.exports = Contextify.readonly(host.require(filename));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
} else {
|
||||
let script;
|
||||
|
||||
try {
|
||||
// Load module
|
||||
let contents = fs.readFileSync(filename, 'utf8');
|
||||
contents = vm._compiler(contents, filename);
|
||||
|
||||
const code = host.STRICT_MODULE_PREFIX + contents + host.MODULE_SUFFIX;
|
||||
|
||||
const ccode = vm._hook('run', [code]);
|
||||
|
||||
// Precompile script
|
||||
script = new Script(ccode, {
|
||||
__proto__: null,
|
||||
filename: filename || 'vm.js',
|
||||
displayErrors: false,
|
||||
importModuleDynamically
|
||||
});
|
||||
|
||||
} catch (ex) {
|
||||
throw Contextify.value(ex);
|
||||
}
|
||||
|
||||
const closure = script.runInContext(global, {
|
||||
__proto__: null,
|
||||
filename: filename || 'vm.js',
|
||||
displayErrors: false,
|
||||
importModuleDynamically
|
||||
});
|
||||
|
||||
// run the script
|
||||
closure(module.exports, module.require, module, filename, dirname);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const _parseExternalOptions = (options) => {
|
||||
if (host.Array.isArray(options)) {
|
||||
return {
|
||||
__proto__: null,
|
||||
external: options,
|
||||
transitive: false
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
__proto__: null,
|
||||
external: options.modules,
|
||||
transitive: options.transitive
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolve filename.
|
||||
*/
|
||||
|
||||
const _resolveFilename = (path) => {
|
||||
if (!path) return null;
|
||||
let hasPackageJson;
|
||||
try {
|
||||
path = pa.resolve(path);
|
||||
|
||||
const exists = fs.existsSync(path);
|
||||
const isdir = exists ? fs.statSync(path).isDirectory() : false;
|
||||
|
||||
// direct file match
|
||||
if (exists && !isdir) return path;
|
||||
|
||||
// load as file
|
||||
|
||||
for (let i = 0; i < vm.options.sourceExtensions.length; i++) {
|
||||
const ext = vm.options.sourceExtensions[i];
|
||||
if (fs.existsSync(`${path}.${ext}`)) return `${path}.${ext}`;
|
||||
}
|
||||
if (fs.existsSync(`${path}.json`)) return `${path}.json`;
|
||||
if (fs.existsSync(`${path}.node`)) return `${path}.node`;
|
||||
|
||||
// load as module
|
||||
|
||||
hasPackageJson = fs.existsSync(`${path}/package.json`);
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
if (hasPackageJson) {
|
||||
let pkg;
|
||||
try {
|
||||
pkg = fs.readFileSync(`${path}/package.json`, 'utf8');
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
try {
|
||||
pkg = parseJSON(pkg);
|
||||
} catch (ex) {
|
||||
throw new VMError(`Module '${path}' has invalid package.json`, 'EMODULEINVALID');
|
||||
}
|
||||
|
||||
let main;
|
||||
if (pkg && pkg.main) {
|
||||
main = _resolveFilename(`${path}/${pkg.main}`);
|
||||
if (!main) main = _resolveFilename(`${path}/index`);
|
||||
} else {
|
||||
main = _resolveFilename(`${path}/index`);
|
||||
}
|
||||
|
||||
return main;
|
||||
}
|
||||
|
||||
// load as directory
|
||||
|
||||
try {
|
||||
for (let i = 0; i < vm.options.sourceExtensions.length; i++) {
|
||||
const ext = vm.options.sourceExtensions[i];
|
||||
if (fs.existsSync(`${path}/index.${ext}`)) return `${path}/index.${ext}`;
|
||||
}
|
||||
|
||||
if (fs.existsSync(`${path}/index.json`)) return `${path}/index.json`;
|
||||
if (fs.existsSync(`${path}/index.node`)) return `${path}/index.node`;
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Builtin require.
|
||||
*/
|
||||
|
||||
const _requireBuiltin = (moduleName) => {
|
||||
if (moduleName === 'buffer') return ({Buffer});
|
||||
if (BUILTINS[moduleName]) return BUILTINS[moduleName].exports; // Only compiled builtins are stored here
|
||||
|
||||
if (moduleName === 'util') {
|
||||
return Contextify.readonly(host.require(moduleName), {
|
||||
// Allows VM context to use util.inherits
|
||||
__proto__: null,
|
||||
inherits: (ctor, superCtor) => {
|
||||
ctor.super_ = superCtor;
|
||||
Object.setPrototypeOf(ctor.prototype, superCtor.prototype);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (moduleName === 'events' || moduleName === 'internal/errors') {
|
||||
let script;
|
||||
try {
|
||||
script = new Script(`(function (exports, require, module, process, internalBinding) {
|
||||
'use strict';
|
||||
const primordials = global;
|
||||
${BUILTIN_MODULES[moduleName]}
|
||||
\n
|
||||
});`, {
|
||||
filename: `${moduleName}.vm.js`
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
// setup module scope
|
||||
const module = BUILTINS[moduleName] = {
|
||||
exports: {},
|
||||
require: _requireBuiltin
|
||||
};
|
||||
|
||||
// run script
|
||||
try {
|
||||
// FIXME binding should be contextified
|
||||
script.runInContext(global)(module.exports, module.require, module, host.process, host.process.binding);
|
||||
} catch (e) {
|
||||
// e could be from inside or outside of sandbox
|
||||
throw new VMError(`Error loading '${moduleName}'`);
|
||||
}
|
||||
return module.exports;
|
||||
}
|
||||
|
||||
return Contextify.readonly(host.require(moduleName));
|
||||
};
|
||||
|
||||
/**
|
||||
* Prepare require.
|
||||
*/
|
||||
|
||||
const _prepareRequire = (currentDirname, parentAllowsTransitive = false) => {
|
||||
const _require = moduleName => {
|
||||
let requireObj;
|
||||
try {
|
||||
const optionsObj = vm.options;
|
||||
if (optionsObj.nesting && moduleName === 'vm2') return {VM: Contextify.readonly(host.VM), NodeVM: Contextify.readonly(host.NodeVM)};
|
||||
requireObj = optionsObj.require;
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
if (!requireObj) throw new VMError(`Access denied to require '${moduleName}'`, 'EDENIED');
|
||||
if (moduleName == null) throw new VMError("Module '' not found.", 'ENOTFOUND');
|
||||
if (typeof moduleName !== 'string') throw new VMError(`Invalid module name '${moduleName}'`, 'EINVALIDNAME');
|
||||
|
||||
let filename;
|
||||
let allowRequireTransitive = false;
|
||||
|
||||
// Mock?
|
||||
|
||||
try {
|
||||
const {mock} = requireObj;
|
||||
if (mock) {
|
||||
const mockModule = mock[moduleName];
|
||||
if (mockModule) {
|
||||
return Contextify.readonly(mockModule);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
// Builtin?
|
||||
|
||||
if (BUILTIN_MODULES[moduleName]) {
|
||||
let allowed;
|
||||
try {
|
||||
const builtinObj = requireObj.builtin;
|
||||
if (host.Array.isArray(builtinObj)) {
|
||||
if (builtinObj.indexOf('*') >= 0) {
|
||||
allowed = builtinObj.indexOf(`-${moduleName}`) === -1;
|
||||
} else {
|
||||
allowed = builtinObj.indexOf(moduleName) >= 0;
|
||||
}
|
||||
} else if (builtinObj) {
|
||||
allowed = builtinObj[moduleName];
|
||||
} else {
|
||||
allowed = false;
|
||||
}
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
if (!allowed) throw new VMError(`Access denied to require '${moduleName}'`, 'EDENIED');
|
||||
|
||||
return _requireBuiltin(moduleName);
|
||||
}
|
||||
|
||||
// External?
|
||||
|
||||
let externalObj;
|
||||
try {
|
||||
externalObj = requireObj.external;
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
if (!externalObj) throw new VMError(`Access denied to require '${moduleName}'`, 'EDENIED');
|
||||
|
||||
if (/^(\.|\.\/|\.\.\/)/.exec(moduleName)) {
|
||||
// Module is relative file, e.g. ./script.js or ../script.js
|
||||
|
||||
if (!currentDirname) throw new VMError('You must specify script path to load relative modules.', 'ENOPATH');
|
||||
|
||||
filename = _resolveFilename(`${currentDirname}/${moduleName}`);
|
||||
} else if (/^(\/|\\|[a-zA-Z]:\\)/.exec(moduleName)) {
|
||||
// Module is absolute file, e.g. /script.js or //server/script.js or C:\script.js
|
||||
|
||||
filename = _resolveFilename(moduleName);
|
||||
} else {
|
||||
// Check node_modules in path
|
||||
|
||||
if (!currentDirname) throw new VMError('You must specify script path to load relative modules.', 'ENOPATH');
|
||||
|
||||
if (typeof externalObj === 'object') {
|
||||
let isWhitelisted;
|
||||
try {
|
||||
const { external, transitive } = _parseExternalOptions(externalObj);
|
||||
|
||||
isWhitelisted = external.some(ext => host.helpers.match(ext, moduleName)) || (transitive && parentAllowsTransitive);
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
if (!isWhitelisted) {
|
||||
throw new VMError(`The module '${moduleName}' is not whitelisted in VM.`, 'EDENIED');
|
||||
}
|
||||
|
||||
allowRequireTransitive = true;
|
||||
}
|
||||
|
||||
// FIXME the paths array has side effects
|
||||
const paths = currentDirname.split(pa.sep);
|
||||
|
||||
while (paths.length) {
|
||||
const path = paths.join(pa.sep);
|
||||
|
||||
// console.log moduleName, "#{path}#{pa.sep}node_modules#{pa.sep}#{moduleName}"
|
||||
|
||||
filename = _resolveFilename(`${path}${pa.sep}node_modules${pa.sep}${moduleName}`);
|
||||
if (filename) break;
|
||||
|
||||
paths.pop();
|
||||
}
|
||||
}
|
||||
|
||||
if (!filename) {
|
||||
let resolveFunc;
|
||||
try {
|
||||
resolveFunc = requireObj.resolve;
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
if (resolveFunc) {
|
||||
let resolved;
|
||||
try {
|
||||
resolved = requireObj.resolve(moduleName, currentDirname);
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
filename = _resolveFilename(resolved);
|
||||
}
|
||||
}
|
||||
if (!filename) throw new VMError(`Cannot find module '${moduleName}'`, 'ENOTFOUND');
|
||||
|
||||
// return cache whenever possible
|
||||
if (CACHE[filename]) return CACHE[filename].exports;
|
||||
|
||||
const dirname = pa.dirname(filename);
|
||||
const extname = pa.extname(filename);
|
||||
|
||||
let allowedModule = true;
|
||||
try {
|
||||
const rootObj = requireObj.root;
|
||||
if (rootObj) {
|
||||
const rootPaths = host.Array.isArray(rootObj) ? rootObj : host.Array.of(rootObj);
|
||||
allowedModule = rootPaths.some(path => host.String.prototype.startsWith.call(dirname, pa.resolve(path)));
|
||||
}
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
if (!allowedModule) {
|
||||
throw new VMError(`Module '${moduleName}' is not allowed to be required. The path is outside the border!`, 'EDENIED');
|
||||
}
|
||||
|
||||
const module = CACHE[filename] = {
|
||||
filename,
|
||||
exports: {},
|
||||
require: _prepareRequire(dirname, allowRequireTransitive)
|
||||
};
|
||||
|
||||
// lookup extensions
|
||||
if (EXTENSIONS[extname]) {
|
||||
EXTENSIONS[extname](module, filename, dirname);
|
||||
return module.exports;
|
||||
}
|
||||
|
||||
throw new VMError(`Failed to load '${moduleName}': Unknown type.`, 'ELOADFAIL');
|
||||
};
|
||||
|
||||
return _require;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prepare sandbox.
|
||||
*/
|
||||
|
||||
// This is a function and not an arrow function, since the original is also a function
|
||||
global.setTimeout = function setTimeout(callback, delay, ...args) {
|
||||
if (typeof callback !== 'function') throw new TypeError('"callback" argument must be a function');
|
||||
let tmr;
|
||||
try {
|
||||
tmr = host.setTimeout(Decontextify.value(() => {
|
||||
// FIXME ...args has side effects
|
||||
callback(...args);
|
||||
}), Decontextify.value(delay));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
const local = Contextify.value(tmr);
|
||||
|
||||
TIMERS.set(local, tmr);
|
||||
return local;
|
||||
};
|
||||
|
||||
global.setInterval = function setInterval(callback, interval, ...args) {
|
||||
if (typeof callback !== 'function') throw new TypeError('"callback" argument must be a function');
|
||||
let tmr;
|
||||
try {
|
||||
tmr = host.setInterval(Decontextify.value(() => {
|
||||
// FIXME ...args has side effects
|
||||
callback(...args);
|
||||
}), Decontextify.value(interval));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
const local = Contextify.value(tmr);
|
||||
|
||||
TIMERS.set(local, tmr);
|
||||
return local;
|
||||
};
|
||||
|
||||
global.setImmediate = function setImmediate(callback, ...args) {
|
||||
if (typeof callback !== 'function') throw new TypeError('"callback" argument must be a function');
|
||||
let tmr;
|
||||
try {
|
||||
tmr = host.setImmediate(Decontextify.value(() => {
|
||||
// FIXME ...args has side effects
|
||||
callback(...args);
|
||||
}));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
const local = Contextify.value(tmr);
|
||||
|
||||
TIMERS.set(local, tmr);
|
||||
return local;
|
||||
};
|
||||
|
||||
global.clearTimeout = function clearTimeout(local) {
|
||||
try {
|
||||
host.clearTimeout(TIMERS.get(local));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
};
|
||||
|
||||
global.clearInterval = function clearInterval(local) {
|
||||
try {
|
||||
host.clearInterval(TIMERS.get(local));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
};
|
||||
|
||||
global.clearImmediate = function clearImmediate(local) {
|
||||
try {
|
||||
host.clearImmediate(TIMERS.get(local));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
};
|
||||
|
||||
function addListener(name, handler) {
|
||||
if (name !== 'beforeExit' && name !== 'exit') {
|
||||
throw new Error(`Access denied to listen for '${name}' event.`);
|
||||
}
|
||||
|
||||
try {
|
||||
host.process.on(name, Decontextify.value(handler));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
const {argv: optionArgv, env: optionsEnv} = options;
|
||||
|
||||
// FIXME wrong class structure
|
||||
global.process = {
|
||||
argv: optionArgv !== undefined ? Contextify.value(optionArgv) : [],
|
||||
title: host.process.title,
|
||||
version: host.process.version,
|
||||
versions: Contextify.readonly(host.process.versions),
|
||||
arch: host.process.arch,
|
||||
platform: host.process.platform,
|
||||
env: optionsEnv !== undefined ? Contextify.value(optionsEnv) : {},
|
||||
pid: host.process.pid,
|
||||
features: Contextify.readonly(host.process.features),
|
||||
nextTick: function nextTick(callback, ...args) {
|
||||
if (typeof callback !== 'function') {
|
||||
throw new Error('Callback must be a function.');
|
||||
}
|
||||
|
||||
try {
|
||||
host.process.nextTick(Decontextify.value(() => {
|
||||
// FIXME ...args has side effects
|
||||
callback(...args);
|
||||
}));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
hrtime: function hrtime(time) {
|
||||
try {
|
||||
return Contextify.value(host.process.hrtime(Decontextify.value(time)));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
cwd: function cwd() {
|
||||
try {
|
||||
return Contextify.value(host.process.cwd());
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
addListener,
|
||||
on: addListener,
|
||||
|
||||
once: function once(name, handler) {
|
||||
if (name !== 'beforeExit' && name !== 'exit') {
|
||||
throw new Error(`Access denied to listen for '${name}' event.`);
|
||||
}
|
||||
|
||||
try {
|
||||
host.process.once(name, Decontextify.value(handler));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
listeners: function listeners(name) {
|
||||
if (name !== 'beforeExit' && name !== 'exit') {
|
||||
// Maybe add ({__proto__:null})[name] to throw when name fails in https://tc39.es/ecma262/#sec-topropertykey.
|
||||
return [];
|
||||
}
|
||||
|
||||
// Filter out listeners, which were not created in this sandbox
|
||||
try {
|
||||
return Contextify.value(host.process.listeners(name).filter(listener => Contextify.isVMProxy(listener)));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
|
||||
removeListener: function removeListener(name, handler) {
|
||||
if (name !== 'beforeExit' && name !== 'exit') {
|
||||
return this;
|
||||
}
|
||||
|
||||
try {
|
||||
host.process.removeListener(name, Decontextify.value(handler));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
umask: function umask() {
|
||||
if (arguments.length) {
|
||||
throw new Error('Access denied to set umask.');
|
||||
}
|
||||
|
||||
try {
|
||||
return Contextify.value(host.process.umask());
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (vm.options.console === 'inherit') {
|
||||
global.console = Contextify.readonly(host.console);
|
||||
} else if (vm.options.console === 'redirect') {
|
||||
global.console = {
|
||||
debug(...args) {
|
||||
try {
|
||||
// FIXME ...args has side effects
|
||||
vm.emit('console.debug', ...Decontextify.arguments(args));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
log(...args) {
|
||||
try {
|
||||
// FIXME ...args has side effects
|
||||
vm.emit('console.log', ...Decontextify.arguments(args));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
info(...args) {
|
||||
try {
|
||||
// FIXME ...args has side effects
|
||||
vm.emit('console.info', ...Decontextify.arguments(args));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
warn(...args) {
|
||||
try {
|
||||
// FIXME ...args has side effects
|
||||
vm.emit('console.warn', ...Decontextify.arguments(args));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
error(...args) {
|
||||
try {
|
||||
// FIXME ...args has side effects
|
||||
vm.emit('console.error', ...Decontextify.arguments(args));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
dir(...args) {
|
||||
try {
|
||||
vm.emit('console.dir', ...Decontextify.arguments(args));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
},
|
||||
time() {},
|
||||
timeEnd() {},
|
||||
trace(...args) {
|
||||
try {
|
||||
// FIXME ...args has side effects
|
||||
vm.emit('console.trace', ...Decontextify.arguments(args));
|
||||
} catch (e) {
|
||||
throw Contextify.value(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
Return contextified require.
|
||||
*/
|
||||
|
||||
return _prepareRequire;
|
||||
})(vm, host);
|
BIN
public/logo.png
Before Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 826 B |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 4.9 KiB |
95
public/package-lock.json
generated
@@ -1,95 +0,0 @@
|
||||
{
|
||||
"name": "public",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"axios": "^0.24.0",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"lodash": "^4.17.21"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "0.24.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
|
||||
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.14.4"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.14.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
|
||||
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": {
|
||||
"version": "0.24.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
|
||||
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.14.4"
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.14.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
|
||||
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w=="
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"requires": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"axios": "^0.24.0",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"lodash": "^4.17.21"
|
||||
}
|
||||
}
|
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"pluginName": "快捷命令",
|
||||
"description": "快速打开软件、网址及运行批处理、shell等脚本,免编写插件使用utools的api及实现UI交互",
|
||||
"main": "index.html",
|
||||
"homepage": "https://github.com/fofolee/uTools-quickcommand",
|
||||
"publishPage": "https://yuanliao.info/d/424",
|
||||
"version": "3.0.0",
|
||||
"development": {
|
||||
"main": "http://127.0.0.1:8080/"
|
||||
},
|
||||
"author": "云之轩",
|
||||
"unpack":"autopep8.py",
|
||||
"logo": "logo.png",
|
||||
"preload": "preload.js",
|
||||
"pluginSetting": {
|
||||
"single": false
|
||||
},
|
||||
"features": [
|
||||
{
|
||||
"code": "configuration",
|
||||
"explain": "新建、编辑或获取快捷命令",
|
||||
"cmds": [ "快捷命令", "QuickCommand"]
|
||||
},
|
||||
{
|
||||
"code": "code",
|
||||
"explain": "运行代码",
|
||||
"cmds": [ "运行代码", "RunCode"]
|
||||
},
|
||||
{
|
||||
"code": "newcommand",
|
||||
"explain": "快速新建快捷命令",
|
||||
"cmds": [ "新建快捷命令", "NewCommand", {
|
||||
"type": "regex",
|
||||
"label": "新建快捷命令",
|
||||
"match": "/^\\{[\\s\\S]*\"program\" *: *\".*\"[\\s\\S]*\"cmd\" *: *\".*\"[\\s\\S]*\\}$/i",
|
||||
"maxNum": 1
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,578 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const child_process = require("child_process")
|
||||
const iconv = require('iconv-lite')
|
||||
const electron = require('electron')
|
||||
const {
|
||||
NodeVM,
|
||||
VM
|
||||
} = require('./lib/vm2')
|
||||
const path = require("path")
|
||||
const axios = require('axios');
|
||||
const pictureCompress = require("./lib/picture-compressor")
|
||||
|
||||
window._ = require("lodash")
|
||||
|
||||
// axios.defaults.adapter = require('axios/lib/adapters/http')
|
||||
|
||||
if (!utools.isWindows()) process.env.PATH += ':/usr/local/bin:/usr/local/sbin'
|
||||
|
||||
// window.startTime = new Date().getTime()
|
||||
|
||||
const shortCodes = [
|
||||
|
||||
open = path => {
|
||||
utools.shellOpenItem(path)
|
||||
},
|
||||
|
||||
locate = path => {
|
||||
utools.shellShowItemInFolder(path);
|
||||
},
|
||||
|
||||
visit = url => {
|
||||
utools.shellOpenExternal(url);
|
||||
},
|
||||
|
||||
system = cmd => {
|
||||
child_process.exec(cmd);
|
||||
},
|
||||
|
||||
message = msg => {
|
||||
utools.showNotification(msg)
|
||||
},
|
||||
|
||||
keyTap = (key, ...modifier) => utools.simulateKeyboardTap(key, ...modifier),
|
||||
|
||||
copyTo = text => {
|
||||
electron.clipboard.writeText(text)
|
||||
},
|
||||
|
||||
send = text => {
|
||||
copyTo(text);
|
||||
quickcommand.simulatePaste();
|
||||
}
|
||||
]
|
||||
|
||||
const ctlKey = utools.isMacOs() ? 'command' : 'control'
|
||||
|
||||
window.quickcommand = {
|
||||
// 模拟复制操作
|
||||
simulateCopy: function() {
|
||||
utools.simulateKeyboardTap('c', ctlKey);
|
||||
},
|
||||
|
||||
// 模拟粘贴操作
|
||||
simulatePaste: function() {
|
||||
utools.simulateKeyboardTap('v', ctlKey);
|
||||
},
|
||||
|
||||
// setTimout 不能在 vm2 中使用,同时在 electron 中有 bug
|
||||
sleep: function(ms) {
|
||||
var start = new Date().getTime()
|
||||
try {
|
||||
// node 16.13.1
|
||||
child_process.execSync(getSleepCodeByShell(ms), {
|
||||
timeout: ms,
|
||||
windowsHide: true
|
||||
})
|
||||
} catch (ex) {}
|
||||
var end = new Date().getTime()
|
||||
return (end - start)
|
||||
},
|
||||
|
||||
// 重写 setTimeout
|
||||
setTimeout: function(callback, ms) {
|
||||
var start = new Date().getTime()
|
||||
child_process.exec(getSleepCodeByShell(ms), {
|
||||
timeout: ms
|
||||
}, (err, stdout, stderr) => {
|
||||
var end = new Date().getTime()
|
||||
callback(end - start)
|
||||
})
|
||||
},
|
||||
|
||||
// 关闭进程
|
||||
kill: function(pid, signal = 'SIGTERM') {
|
||||
process.kill(pid, signal)
|
||||
},
|
||||
|
||||
// dom 解析
|
||||
htmlParse: function(html) {
|
||||
return new DOMParser().parseFromString(html, 'text/html')
|
||||
},
|
||||
|
||||
// 下载文件
|
||||
downloadFile: function(url, file = {}) {
|
||||
return new Promise((reslove, reject) => {
|
||||
if (file instanceof Object) file = utools.showSaveDialog(JSON.parse(JSON.stringify(file)))
|
||||
axios({
|
||||
method: 'get',
|
||||
url: url,
|
||||
responseType: 'arraybuffer'
|
||||
}).then(res => {
|
||||
var filebuffer = Buffer.from(res.data)
|
||||
fs.writeFile(file, filebuffer, err => {
|
||||
if (err) reject(err)
|
||||
else reslove(filebuffer)
|
||||
})
|
||||
}).catch(err => {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 上传文件
|
||||
uploadFile: function(url, file = {}, name = 'file', formData = {}) {
|
||||
return new Promise((reslove, reject) => {
|
||||
var objfile
|
||||
if (file instanceof File) {
|
||||
objfile = file
|
||||
} else {
|
||||
if (file instanceof Object) file = utools.showOpenDialog(JSON.parse(JSON.stringify(file)))[0]
|
||||
if (!fs.existsSync(file)) return reject('文件不存在')
|
||||
var arraybuffer = fs.readFileSync(file).buffer
|
||||
var objfile = new File([arraybuffer], path.basename(file))
|
||||
}
|
||||
var form = new FormData();
|
||||
form.append(name, objfile)
|
||||
var keys = Object.keys(formData)
|
||||
if (keys.length) keys.forEach(k => form.append(k, formData[k]))
|
||||
axios.post(url, form, {
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'Content-Type': `multipart/form-data; boundary=${formData._boundary}`,
|
||||
}
|
||||
}).then(res => {
|
||||
reslove(res)
|
||||
}).catch(err => {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 载入在线资源
|
||||
loadRemoteScript: async function(url, forceUpdate = false) {
|
||||
if (!/^((ht|f)tps?):\/\/([\w\-]+(\.[\w\-]+)*\/)*[\w\-]+(\.[\w\-]+)*\/?(\?([\w\-\.,@?^=%&:\/~\+#]*)+)?/.test(url)) throw 'url 不合法'
|
||||
let remote = url
|
||||
let root = path.join(os.tmpdir(), 'qcRemoteScript')
|
||||
if (!fs.existsSync(root)) fs.mkdirSync(root)
|
||||
let local = path.join(root, require('crypto').createHash('md5').update(url).digest('hex'))
|
||||
if (forceUpdate || !fs.existsSync(local)) await this.downloadFile(remote, local)
|
||||
return require(local)
|
||||
}
|
||||
}
|
||||
|
||||
// 运行vbs脚本
|
||||
if (process.platform == 'win32') quickcommand.runVbs = function(script) {
|
||||
return new Promise((reslove, reject) => {
|
||||
var tempfile = path.join(os.tmpdir(), 'TempVBSScript.vbs')
|
||||
fs.writeFile(tempfile, iconv.encode(script, 'gbk'), err => {
|
||||
child_process.exec(`cscript.exe /nologo "${tempfile}"`, {
|
||||
encoding: "buffer"
|
||||
}, (err, stdout, stderr) => {
|
||||
if (err) reject(iconv.decode(stderr, 'gbk'))
|
||||
else reslove(iconv.decode(stdout, 'gbk'))
|
||||
});
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
window.temporaryStore = {
|
||||
listeners: {}
|
||||
}
|
||||
|
||||
window.temporaryStoreSoldOut = () => {
|
||||
_.forIn(temporaryStore.listeners, (listener, key) => {
|
||||
document.removeEventListener(...listener)
|
||||
})
|
||||
window.temporaryStore = {
|
||||
listeners: {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// python -c
|
||||
window.runPythonCommand = py => {
|
||||
try {
|
||||
let result = child_process.execFileSync("python", ["-c", py], {
|
||||
windowsHide: true,
|
||||
encoding: 'buffer'
|
||||
})
|
||||
return iconv.decode(result, utools.isWindows() ? 'gbk' : 'utf8').trim()
|
||||
} catch (e) {
|
||||
alert(e)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// 在终端中执行
|
||||
if (process.platform !== 'linux') quickcommand.runInTerminal = function(cmdline, dir) {
|
||||
let command = getCommandToLaunchTerminal(cmdline, dir)
|
||||
child_process.exec(command)
|
||||
}
|
||||
|
||||
let getCommandToLaunchTerminal = (cmdline, dir) => {
|
||||
let cd = ''
|
||||
if (utools.isWindows()) {
|
||||
let appPath = path.join(utools.getPath('home'), '/AppData/Local/Microsoft/WindowsApps/')
|
||||
// 直接 existsSync wt.exe 无效
|
||||
if (fs.existsSync(appPath) && fs.readdirSync(appPath).includes('wt.exe')) {
|
||||
cmdline = cmdline.replace(/"/g, `\\"`)
|
||||
if (dir) cd = `-d "${dir.replace(/\\/g, '/')}"`
|
||||
command = `${appPath}wt.exe ${cd} cmd /k "${cmdline}"`
|
||||
} else {
|
||||
cmdline = cmdline.replace(/"/g, `^"`)
|
||||
if (dir) cd = `cd /d "${dir.replace(/\\/g, '/')}" &&`
|
||||
command = `${cd} start "" cmd /k "${cmdline}"`
|
||||
}
|
||||
} else {
|
||||
cmdline = cmdline.replace(/"/g, `\\"`)
|
||||
if (dir) cd = `cd ${dir.replace(/ /g, `\\\\ `)} &&`
|
||||
if (fs.existsSync('/Applications/iTerm.app')) {
|
||||
command = `osascript -e 'tell application "iTerm"
|
||||
create window with default profile
|
||||
tell current session of current window to write text "clear && ${cd} ${cmdline}"
|
||||
end tell'`
|
||||
} else {
|
||||
command = `osascript -e 'tell application "Terminal"
|
||||
do script "clear && ${cd} ${cmdline}"
|
||||
activate
|
||||
end tell'`
|
||||
}
|
||||
}
|
||||
console.log(command);
|
||||
return command
|
||||
}
|
||||
|
||||
window.pluginInfo = () => {
|
||||
return JSON.parse(fs.readFileSync(path.join(__dirname, 'plugin.json')))
|
||||
}
|
||||
|
||||
let getSleepCodeByShell = ms => {
|
||||
var cmd, tempFilePath
|
||||
if (utools.isWindows()) {
|
||||
tempFilePath = getQuickcommandTempFile('vbs')
|
||||
cmd = `echo set ws=CreateObject("Wscript.Shell") > ${tempFilePath} && echo Wscript.sleep ${ms} >> ${tempFilePath} && cscript /nologo ${tempFilePath}`
|
||||
} else {
|
||||
cmd = `sleep ${ms / 1000}`
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// 屏蔽危险函数
|
||||
window.getuToolsLite = () => {
|
||||
var utoolsLite = Object.assign({}, utools)
|
||||
if (utools.isDev()) return utoolsLite
|
||||
// 数据库相关接口
|
||||
delete utoolsLite.db
|
||||
delete utoolsLite.dbStorage
|
||||
delete utoolsLite.removeFeature
|
||||
delete utoolsLite.setFeature
|
||||
delete utoolsLite.onDbPull
|
||||
// 支付相关接口
|
||||
delete utoolsLite.fetchUserServerTemporaryToken
|
||||
delete utoolsLite.getUserServerTemporaryToken
|
||||
delete utoolsLite.openPayment
|
||||
delete utoolsLite.fetchUserPayments
|
||||
return utoolsLite
|
||||
}
|
||||
|
||||
let getSandboxFuns = () => {
|
||||
var sandbox = {
|
||||
utools: getuToolsLite(),
|
||||
quickcommand: quickcommand,
|
||||
electron: electron,
|
||||
axios: axios,
|
||||
Audio: Audio,
|
||||
fetch: fetch
|
||||
}
|
||||
shortCodes.forEach(f => {
|
||||
sandbox[f.name] = f
|
||||
})
|
||||
return sandbox
|
||||
}
|
||||
|
||||
let createNodeVM = () => {
|
||||
var sandbox = getSandboxFuns()
|
||||
const vm = new NodeVM({
|
||||
require: {
|
||||
external: true,
|
||||
builtin: ["*"],
|
||||
},
|
||||
console: 'redirect',
|
||||
env: process.env,
|
||||
sandbox: sandbox,
|
||||
});
|
||||
return vm
|
||||
}
|
||||
|
||||
let stringifyAll = item => {
|
||||
var cache = [];
|
||||
var string = JSON.stringify(item, (key, value) => {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (cache.indexOf(value) !== -1) return
|
||||
cache.push(value);
|
||||
}
|
||||
return value;
|
||||
}, '\t')
|
||||
if (string != "{}") return string
|
||||
else return item.toString()
|
||||
}
|
||||
|
||||
let parseItem = item => {
|
||||
if (typeof item == "object") {
|
||||
if (Buffer.isBuffer(item)) {
|
||||
var bufferString = `[Buffer ${item.slice(0, 50).toString('hex').match(/\w{1,2}/g).join(" ")}`
|
||||
if (item.length > 50) bufferString += `... ${(item.length / 1000).toFixed(2)}kb`
|
||||
return bufferString + ']'
|
||||
} else if (item instanceof ArrayBuffer) {
|
||||
return `ArrayBuffer(${item.byteLength})`
|
||||
} else if (item instanceof Blob) {
|
||||
return `Blob {size: ${item.size}, type: "${item.type}"}`
|
||||
} else {
|
||||
try {
|
||||
return stringifyAll(item)
|
||||
} catch (error) {}
|
||||
}
|
||||
} else if (typeof item == "undefined") {
|
||||
return "undefined"
|
||||
}
|
||||
return item.toString()
|
||||
}
|
||||
|
||||
window.convertFilePathToUtoolsPayload = files => files.map(file => {
|
||||
let isFile = fs.statSync(file).isFile()
|
||||
return {
|
||||
isFile: isFile,
|
||||
isDirectory: !isFile,
|
||||
name: path.basename(file),
|
||||
path: file
|
||||
}
|
||||
})
|
||||
|
||||
let parseStdout = stdout => stdout.map(x => parseItem(x)).join("\n")
|
||||
|
||||
window.VmEval = (cmd, sandbox = {}) => new VM({
|
||||
sandbox: sandbox
|
||||
}).run(cmd)
|
||||
|
||||
// The vm module of Node.js is deprecated in the renderer process and will be removed
|
||||
window.runCodeInVm = (cmd, cb) => {
|
||||
const vm = createNodeVM()
|
||||
//重定向 console
|
||||
vm.on('console.log', (...stdout) => {
|
||||
console.log(stdout);
|
||||
cb(parseStdout(stdout), null)
|
||||
});
|
||||
|
||||
vm.on('console.error', stderr => {
|
||||
cb(null, stderr.toString())
|
||||
});
|
||||
|
||||
let liteErr = e => {
|
||||
if (!e) return
|
||||
return e.stack.replace(/([ ] +at.+)|(.+\.js:\d+)/g, '').trim()
|
||||
}
|
||||
|
||||
// 错误处理
|
||||
try {
|
||||
vm.run(cmd, path.join(__dirname, 'preload.js'));
|
||||
} catch (e) {
|
||||
console.log('Error: ', e)
|
||||
cb(null, liteErr(e))
|
||||
}
|
||||
|
||||
let cbUnhandledError = e => {
|
||||
removeAllListener()
|
||||
console.log('UnhandledError: ', e)
|
||||
cb(null, liteErr(e.error))
|
||||
}
|
||||
|
||||
let cbUnhandledRejection = e => {
|
||||
removeAllListener()
|
||||
console.log('UnhandledRejection: ', e)
|
||||
cb(null, liteErr(e.reason))
|
||||
}
|
||||
|
||||
let removeAllListener = () => {
|
||||
window.removeEventListener('error', cbUnhandledError)
|
||||
window.removeEventListener('unhandledrejection', cbUnhandledRejection)
|
||||
delete window.isWatchingError
|
||||
}
|
||||
|
||||
if (!window.isWatchingError) {
|
||||
window.addEventListener('error', cbUnhandledError)
|
||||
window.addEventListener('unhandledrejection', cbUnhandledRejection)
|
||||
window.isWatchingError = true
|
||||
}
|
||||
}
|
||||
|
||||
window.htmlEncode = (value) => {
|
||||
return String(value).replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """)
|
||||
}
|
||||
|
||||
window.hexEncode = text => Buffer.from(text, 'utf8').toString('hex')
|
||||
window.hexDecode = text => Buffer.from(text, 'hex').toString('utf8')
|
||||
|
||||
window.processPlatform = process.platform
|
||||
|
||||
window.getQuickcommandTempFile = ext => {
|
||||
return path.join(os.tmpdir(), `quickcommandTempFile.${ext}`)
|
||||
}
|
||||
|
||||
window.getBase64Ico = async (filepath, compressed = true) => {
|
||||
let sourceImage, ext = path.extname(filepath).slice(1)
|
||||
if (['png', 'jpg', 'jpeg', 'bmp', 'ico', 'gif', 'svg'].includes(ext)) {
|
||||
if (ext == 'svg') ext = 'svg+xml'
|
||||
sourceImage = `data:image/${ext};base64,` + fs.readFileSync(filepath, 'base64')
|
||||
if (ext == 'png') return sourceImage
|
||||
} else {
|
||||
sourceImage = utools.getFileIcon(filepath)
|
||||
return sourceImage
|
||||
}
|
||||
if (!compressed) return sourceImage
|
||||
let compressedImage = await getCompressedIco(sourceImage)
|
||||
return compressedImage
|
||||
}
|
||||
|
||||
let getCompressedIco = async (img, width = 80) => {
|
||||
let compressedImage = await pictureCompress({
|
||||
img: img,
|
||||
width: width,
|
||||
height: width,
|
||||
type: 'png',
|
||||
quality: 1
|
||||
})
|
||||
return compressedImage.img
|
||||
}
|
||||
|
||||
window.getFileInfo = options => {
|
||||
var file
|
||||
if (options.type == 'file') {
|
||||
file = options.argvs
|
||||
} else if (options.type == 'dialog') {
|
||||
var dialog = utools.showOpenDialog(options.argvs);
|
||||
if (!dialog) return false
|
||||
file = dialog[0]
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
var information = {
|
||||
name: path.basename(file),
|
||||
ext: path.extname(file),
|
||||
path: file
|
||||
}
|
||||
if (options.readfile) {
|
||||
var codec = (information.ext == '.bat' || information == '.ps1') ? 'gbk' : 'utf8'
|
||||
information.data = iconv.decode(fs.readFileSync(file), codec)
|
||||
}
|
||||
return information
|
||||
}
|
||||
|
||||
window.getCurrentFolderPathFix = () => {
|
||||
let pwd = utools.getCurrentFolderPath()
|
||||
let pwdFix = pwd ? pwd : path.join(utools.getPath('home'), 'desktop')
|
||||
return pwdFix.replace(/\\/g, '\\\\')
|
||||
}
|
||||
|
||||
window.getMatchedFilesFix = payload => {
|
||||
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('$', '$$$'))
|
||||
})
|
||||
}
|
||||
|
||||
window.saveFile = (content, file) => {
|
||||
if (file instanceof Object) file = utools.showSaveDialog(file)
|
||||
if (!file) return false
|
||||
try {
|
||||
fs.writeFileSync(file, content)
|
||||
return true
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
window.getSelectFile = hwnd => {
|
||||
if (utools.isWindows()) {
|
||||
var cmd = `powershell.exe -NoProfile "(New-Object -COM 'Shell.Application').Windows() | Where-Object { $_.HWND -eq ${hwnd} } | Select-Object -Expand Document | select @{ n='SelectItems'; e={$_.SelectedItems()} } | select -Expand SelectItems | select -Expand Path "`;
|
||||
let result = child_process.execSync(cmd, {
|
||||
encoding: "buffer",
|
||||
windowsHide: true
|
||||
})
|
||||
return iconv.decode(result, 'GBK').trim().replace(/\\/g, '/');
|
||||
} else {
|
||||
var cmd = `osascript -e 'tell application "Finder" to set selectedItems to selection as alias list
|
||||
if selectedItems is {} then return
|
||||
set parentPath to do shell script "dirname " & quoted form of POSIX path of (item 1 of selectedItems)
|
||||
set pathData to ""
|
||||
repeat with theItem in selectedItems
|
||||
set pathData to pathData & POSIX path of theItem & linefeed
|
||||
end repeat
|
||||
'
|
||||
`
|
||||
let result = child_process.execSync(cmd, {
|
||||
encoding: "utf8",
|
||||
windowsHide: true
|
||||
})
|
||||
console.log(result);
|
||||
return result ? result.trim() : ""
|
||||
}
|
||||
}
|
||||
|
||||
window.clipboardReadText = () => electron.clipboard.readText()
|
||||
|
||||
window.runCodeFile = (cmd, option, terminal, callback) => {
|
||||
var bin = option.bin,
|
||||
argv = option.argv,
|
||||
ext = option.ext,
|
||||
charset = option.charset,
|
||||
scptarg = option.scptarg || "";
|
||||
let script = getQuickcommandTempFile(ext)
|
||||
// 批处理和 powershell 默认编码为 GBK, 解决批处理的换行问题
|
||||
if (charset.scriptCode) cmd = iconv.encode(cmd.replace(/\n/g, '\r\n'), charset.scriptCode);
|
||||
fs.writeFileSync(script, cmd);
|
||||
// var argvs = [script]
|
||||
// if (argv) {
|
||||
// argvs = argv.split(' ')
|
||||
// argvs.push(script);
|
||||
// }
|
||||
var child, cmdline
|
||||
if (bin.slice(-7) == 'csc.exe') {
|
||||
cmdline = `${bin} ${argv} /out:"${script.slice(0, -2) + 'exe'}" "${script}" && "${script.slice(0, -2) + 'exe'}" ${scptarg}`
|
||||
} else if (bin == 'gcc') {
|
||||
var suffix = utools.isWindows() ? '.exe' : ''
|
||||
cmdline = `${bin} ${argv} "${script.slice(0, -2)}" "${script}" && "${script.slice(0, -2) + suffix}" ${scptarg}`
|
||||
} else if (utools.isWindows() && bin == 'bash') {
|
||||
cmdline = `${bin} ${argv} "${script.replace(/\\/g, '/').replace(/C:/i, '/mnt/c')}" ${scptarg}`
|
||||
} else {
|
||||
cmdline = `${bin} ${argv} "${script}" ${scptarg}`
|
||||
}
|
||||
// 在终端中输出
|
||||
if (terminal) cmdline = getCommandToLaunchTerminal(cmdline)
|
||||
child = child_process.spawn(cmdline, {
|
||||
encoding: 'buffer',
|
||||
shell: true
|
||||
})
|
||||
// var chunks = [],
|
||||
// err_chunks = [];
|
||||
console.log('running: ' + cmdline);
|
||||
child.stdout.on('data', chunk => {
|
||||
if (charset.outputCode) chunk = iconv.decode(chunk, charset.outputCode)
|
||||
callback(chunk.toString(), null)
|
||||
// chunks.push(chunk)
|
||||
})
|
||||
child.stderr.on('data', stderr => {
|
||||
if (charset.outputCode) stderr = iconv.decode(stderr, charset.outputCode)
|
||||
callback(null, stderr.toString())
|
||||
// err_chunks.push(err_chunk)
|
||||
})
|
||||
// child.on('close', code => {
|
||||
// let stdout = chunks.join("");
|
||||
// let stderr = err_chunks.join("");
|
||||
// callback(stdout, stderr)
|
||||
// })
|
||||
}
|