feat: add builder configuration file
feat: add cocos-service configuration file feat: add device configuration file feat: add engine configuration file feat: add information configuration file feat: add program configuration file feat: add project configuration file feat: add TypeScript configuration file
This commit is contained in:
104
extensions/quick-compress-image/.gitignore
vendored
Normal file
104
extensions/quick-compress-image/.gitignore
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and *not* Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
21
extensions/quick-compress-image/LICENSE
Normal file
21
extensions/quick-compress-image/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 McvCar
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
2
extensions/quick-compress-image/README.md
Normal file
2
extensions/quick-compress-image/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# quickCompressImage
|
||||
这是cocos creator 快速压缩图片插件
|
||||
9
extensions/quick-compress-image/i18n/en.js
Normal file
9
extensions/quick-compress-image/i18n/en.js
Normal file
@@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
/**
|
||||
* menu
|
||||
*/
|
||||
'name': 'x',
|
||||
'compressPicture': 'Compress Picture',
|
||||
'menu': 'Compress Picture',
|
||||
'setting': 'Setting',
|
||||
};
|
||||
9
extensions/quick-compress-image/i18n/zh.js
Normal file
9
extensions/quick-compress-image/i18n/zh.js
Normal file
@@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
/**
|
||||
* 菜单
|
||||
*/
|
||||
'name': 'x',
|
||||
'compressPicture': '压缩图片',
|
||||
'menu': '快闪·压缩图片',
|
||||
'setting': '设置',
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "mozjpeg",
|
||||
"version": "8.0.0",
|
||||
"description": "mozjpeg wrapper that makes it seamlessly available as a local dependency",
|
||||
"license": "MIT",
|
||||
"repository": "imagemin/mozjpeg-bin",
|
||||
"type": "module",
|
||||
"exports": "./index.js",
|
||||
"bin": "cli.js",
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "node lib/install.js",
|
||||
"test": "xo && ava --timeout=120s",
|
||||
"build-linux": "docker build --tag imagemin/mozjpeg docker && docker run --rm --volume $(pwd)/vendor/linux:/src/out imagemin/mozjpeg cp cjpeg /src/out"
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"cli.js",
|
||||
"lib",
|
||||
"vendor/source"
|
||||
],
|
||||
"keywords": [
|
||||
"imagemin",
|
||||
"jpeg",
|
||||
"jpg",
|
||||
"img",
|
||||
"image",
|
||||
"compress",
|
||||
"minify",
|
||||
"mozjpeg",
|
||||
"optimize"
|
||||
],
|
||||
"dependencies": {
|
||||
"bin-build": "^3.0.0",
|
||||
"bin-wrapper": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ava": "^3.8.0",
|
||||
"bin-check": "^4.1.0",
|
||||
"compare-size": "^3.0.0",
|
||||
"execa": "^5.1.1",
|
||||
"tempy": "^2.0.0",
|
||||
"xo": "^0.45.0"
|
||||
},
|
||||
"ava": {
|
||||
"serial": true
|
||||
}
|
||||
}
|
||||
13098
extensions/quick-compress-image/lib/imagemin.min.js
vendored
Normal file
13098
extensions/quick-compress-image/lib/imagemin.min.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
/*!
|
||||
* fill-range <https://github.com/jonschlinkert/fill-range>
|
||||
*
|
||||
* Copyright (c) 2014-present, Jon Schlinkert.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* is-extglob <https://github.com/jonschlinkert/is-extglob>
|
||||
*
|
||||
* Copyright (c) 2014-2016, Jon Schlinkert.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* is-glob <https://github.com/jonschlinkert/is-glob>
|
||||
*
|
||||
* Copyright (c) 2014-2017, Jon Schlinkert.
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* is-number <https://github.com/jonschlinkert/is-number>
|
||||
*
|
||||
* Copyright (c) 2014-present, Jon Schlinkert.
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* to-regex-range <https://github.com/micromatch/to-regex-range>
|
||||
*
|
||||
* Copyright (c) 2015-present, Jon Schlinkert.
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
|
||||
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
||||
|
||||
/*! queue-microtask. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
||||
|
||||
/*! run-parallel. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
||||
77
extensions/quick-compress-image/lib/imageminApi.js
Normal file
77
extensions/quick-compress-image/lib/imageminApi.js
Normal file
@@ -0,0 +1,77 @@
|
||||
let Path = require('path')
|
||||
let Fs = require('fs');
|
||||
let imageminApi = require('../lib/imagemin.min')
|
||||
let packageCfg = require('../package.json')
|
||||
|
||||
module.exports = {
|
||||
|
||||
async getZipRate(){
|
||||
let rate = parseInt(await Editor.Profile.getConfig(packageCfg.name,'zipRate')) || 30;
|
||||
if(rate<0){
|
||||
rate = 1;
|
||||
}else if(rate>100){
|
||||
rate = 100
|
||||
}
|
||||
|
||||
return rate;
|
||||
},
|
||||
|
||||
/**
|
||||
* 调用压缩工具api
|
||||
* @param {Array<{file:string,uuid:string}>} fileInfo
|
||||
*/
|
||||
async compressPicture(arrList,imageFileList){
|
||||
let rate = await this.getZipRate();
|
||||
let pngRate = rate*0.01;
|
||||
|
||||
console.log("压缩值:",rate+"%");
|
||||
|
||||
imageminApi.imagemin(arrList, {
|
||||
plugins: [
|
||||
imageminApi.imageminMozjpeg({ quality: rate }), //压缩质量(0,1)
|
||||
imageminApi.imageminPngquant({
|
||||
quality: [pngRate, Math.min(pngRate+0.25,1)] //压缩质量(0,1)
|
||||
})
|
||||
]
|
||||
|
||||
}).then((arrRes) => {
|
||||
console.log("压缩成功,详情:\n")
|
||||
for (let i = 0; i < arrRes.length; i++) {
|
||||
const res = arrRes[i];
|
||||
this.onCompressedSucceed(imageFileList,res)
|
||||
}
|
||||
|
||||
}).catch(err => {
|
||||
console.log("压缩失败:",err)
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 压缩成功
|
||||
* @param {Array<{file:string,uuid:string,size:number}>} imageFileList
|
||||
* @param {Object<{data:Buffer,sourcePath:string}>} res
|
||||
*/
|
||||
onCompressedSucceed(imageFileList,res){
|
||||
let desc = ""
|
||||
for (let i = 0; i < imageFileList.length; i++) {
|
||||
const fileInfo = imageFileList[i];
|
||||
if(fileInfo.file.replace(/\\/g,'/') == res.sourcePath){
|
||||
const fileName = Path.basename(fileInfo.file);
|
||||
const newSize = res.data.byteLength;
|
||||
const rate = (fileInfo.size-newSize)/fileInfo.size;
|
||||
const oldMb = (fileInfo.size / 1024).toFixed(1) + " KB";
|
||||
const newMb = (newSize / 1024).toFixed(1) + " KB";
|
||||
if(newSize < fileInfo.size){
|
||||
Fs.writeFileSync(res.sourcePath, res.data)
|
||||
desc += `${fileName}、压缩率:${parseInt(rate*100)}%、压缩前后大小:${newMb} / ${oldMb}`
|
||||
}else{
|
||||
desc += `${fileName}、无法继续压缩`
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
console.log(desc);
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
89
extensions/quick-compress-image/lib/tinypngApi.js
Normal file
89
extensions/quick-compress-image/lib/tinypngApi.js
Normal file
@@ -0,0 +1,89 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const https = require("https");
|
||||
const URL = require("url").URL;
|
||||
const EventEmitter = require("events");
|
||||
const err = (msg) => new EventEmitter().emit("error", msg);
|
||||
|
||||
/**
|
||||
* TinyPng 远程压缩 HTTPS 请求的配置生成方法
|
||||
*/
|
||||
|
||||
function getAjaxOptions() {
|
||||
return {
|
||||
method: "POST",
|
||||
hostname: "tinypng.com",
|
||||
path: "/web/shrink",
|
||||
headers: {
|
||||
rejectUnauthorized: false,
|
||||
"X-Forwarded-For": Array(4)
|
||||
.fill(1)
|
||||
.map(() => parseInt(Math.random() * 254 + 1))
|
||||
.join("."),
|
||||
"Postman-Token": Date.now(),
|
||||
"Cache-Control": "no-cache",
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* TinyPng 远程压缩 HTTPS 请求
|
||||
* @param {string} img 待处理的文件
|
||||
* @success {
|
||||
* "input": { "size": 887, "type": "image/png" },
|
||||
* "output": { "size": 785, "type": "image/png", "width": 81, "height": 81, "ratio": 0.885, "url": "https://tinypng.com/web/output/7aztz90nq5p9545zch8gjzqg5ubdatd6" }
|
||||
* }
|
||||
* @error {"error": "Bad request", "message" : "Request is invalid"}
|
||||
*/
|
||||
function fileUpload(imgPath) {
|
||||
let req = https.request(getAjaxOptions(), (res) => {
|
||||
res.on("data", (buf) => {
|
||||
let obj = JSON.parse(buf.toString());
|
||||
if (obj.error)
|
||||
console.log(`压缩失败!\n 当前文件:${imgPath} \n ${obj.message}`);
|
||||
else fileUpdate(imgPath, obj);
|
||||
});
|
||||
});
|
||||
|
||||
req.write(fs.readFileSync(imgPath), "binary");
|
||||
req.on("error", (e) =>
|
||||
console.log(`请求错误! 当前文件:${path.basename(imgPath)} \n`, e)
|
||||
);
|
||||
req.end();
|
||||
}
|
||||
|
||||
// 该方法被循环调用,请求图片数据
|
||||
function fileUpdate(entryImgPath, obj) {
|
||||
let options = new URL(obj.output.url);
|
||||
let req = https.request(options, (res) => {
|
||||
let body = "";
|
||||
res.setEncoding("binary");
|
||||
res.on("data", (data) => (body += data));
|
||||
res.on("end", () => {
|
||||
fs.writeFile(entryImgPath, body, "binary", (err) => {
|
||||
if (err) return console.error(err);
|
||||
let log = `压缩成功:`;
|
||||
log += `${path.basename(entryImgPath)}、压缩率:${ ((1 - obj.output.ratio) * 100).toFixed(2) }%、压缩前后大小:${(obj.output.size / 1024).toFixed(2)} / ${(obj.input.size / 1024).toFixed(2)}`
|
||||
console.log(log);
|
||||
});
|
||||
});
|
||||
});
|
||||
req.on("error", (e) => console.warn('压缩失败:',path.basename(entryImgPath) ,e));
|
||||
req.end();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* 调用压缩工具api
|
||||
* @param {Array<{file:string,uuid:string}>} fileInfo
|
||||
*/
|
||||
async compressPicture(arrList,imageFileList){
|
||||
for (let i = 0; i < arrList.length; i++) {
|
||||
const imgPath = arrList[i];
|
||||
fileUpload(imgPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
extensions/quick-compress-image/lib/vendor/cjpeg
vendored
Normal file
BIN
extensions/quick-compress-image/lib/vendor/cjpeg
vendored
Normal file
Binary file not shown.
BIN
extensions/quick-compress-image/lib/vendor/cjpeg.exe
vendored
Normal file
BIN
extensions/quick-compress-image/lib/vendor/cjpeg.exe
vendored
Normal file
Binary file not shown.
BIN
extensions/quick-compress-image/lib/vendor/pngquant
vendored
Normal file
BIN
extensions/quick-compress-image/lib/vendor/pngquant
vendored
Normal file
Binary file not shown.
BIN
extensions/quick-compress-image/lib/vendor/pngquant-ppc
vendored
Normal file
BIN
extensions/quick-compress-image/lib/vendor/pngquant-ppc
vendored
Normal file
Binary file not shown.
BIN
extensions/quick-compress-image/lib/vendor/pngquant.exe
vendored
Normal file
BIN
extensions/quick-compress-image/lib/vendor/pngquant.exe
vendored
Normal file
Binary file not shown.
10651
extensions/quick-compress-image/package-lock.json
generated
Normal file
10651
extensions/quick-compress-image/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
78
extensions/quick-compress-image/package.json
Normal file
78
extensions/quick-compress-image/package.json
Normal file
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"name": "quick-compress-image",
|
||||
"package_version": 2,
|
||||
"version": "1.0.1",
|
||||
"description": "Compress a picture",
|
||||
"author": "嘉年华",
|
||||
"main": "./src/browser.js",
|
||||
"contributions": {
|
||||
"menu": [{
|
||||
"path": "i18n:menu.extension/i18n:quick-compress-image.menu",
|
||||
"label": "i18n:quick-compress-image.setting",
|
||||
"message": "setting"
|
||||
}],
|
||||
"messages": {
|
||||
"setting": {
|
||||
"methods": ["setting"]
|
||||
}
|
||||
},
|
||||
"assets": {
|
||||
"menu": {
|
||||
"methods": "./src/assets-menu.js",
|
||||
"createMenu": "onCreateMenu",
|
||||
"assetMenu": "onAssetMenu"
|
||||
}
|
||||
},
|
||||
|
||||
"profile": {
|
||||
"editor": {
|
||||
"zipRate": {
|
||||
"default": 70,
|
||||
"label": "压缩值(0-100%)"
|
||||
},
|
||||
"zipMode":{
|
||||
"default": 0,
|
||||
"label": "压缩模式"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"preferences": {
|
||||
"label" : "图片压缩",
|
||||
"properties": {
|
||||
"zipRate": {
|
||||
"ui": "ui-num-input"
|
||||
},
|
||||
"zipMode": {
|
||||
"ui": "ui-select",
|
||||
"items": [
|
||||
{
|
||||
"value": 0,
|
||||
"label": "Imagemin"
|
||||
},
|
||||
{
|
||||
"value": 1,
|
||||
"label": "Tinypng"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"panels": {
|
||||
"default": {
|
||||
"title": "快闪·压缩图片",
|
||||
"type": "dockable",
|
||||
"main": "src/setting-panel",
|
||||
"size": {
|
||||
"width": 280,
|
||||
"height": 260
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
},
|
||||
"devDependencies": {
|
||||
}
|
||||
}
|
||||
113
extensions/quick-compress-image/src/assets-menu.js
Normal file
113
extensions/quick-compress-image/src/assets-menu.js
Normal file
@@ -0,0 +1,113 @@
|
||||
let Path = require('path')
|
||||
let Fs = require('fs');
|
||||
let imageminApi = require('../lib/imageminApi')
|
||||
let tinypngApi = require('../lib/tinypngApi')
|
||||
let statistical = require('./tools/statistical')
|
||||
let packageCfg = require('../package.json');
|
||||
const tools = require('./tools/tools');
|
||||
|
||||
let AssetsMenu = {
|
||||
|
||||
onAssetMenu(assetInfo) {
|
||||
if(assetInfo.importer != 'image'){
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
label: 'i18n:quick-compress-image.compressPicture',
|
||||
enabled: true,
|
||||
click() {
|
||||
if(confirm('确定压缩图片?')){
|
||||
AssetsMenu.onStartCompressPicture(assetInfo)
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
onCreateMenu(assetInfo) {
|
||||
statistical.countStartupTimes();
|
||||
return [];
|
||||
},
|
||||
|
||||
isImageFile(filePath){
|
||||
let extname = Path.extname(filePath).toLocaleLowerCase()
|
||||
if(extname == '.png' || extname == '.jpg' || extname == '.jpeg'){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
getFileSize(fsPath){
|
||||
try {
|
||||
return Fs.statSync(fsPath).size
|
||||
} catch (error) {
|
||||
return -1
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 返回选中的图片
|
||||
* @param {*} assetInfo
|
||||
* @returns {Promise<Array<{file:string,uuid:string,size:number}>>}
|
||||
*/
|
||||
async getImageFileList(assetInfo){
|
||||
let fileUuidList = Editor.Selection.getSelected('asset');
|
||||
let imageFileList = [];
|
||||
|
||||
if(fileUuidList.includes(assetInfo.uuid)){
|
||||
for (let i = 0; i < fileUuidList.length; i++) {
|
||||
const uuid = fileUuidList[i];
|
||||
const fsPath = await Editor.Message.request("asset-db",'query-path',uuid);
|
||||
|
||||
if(fsPath && this.isImageFile(fsPath)){
|
||||
imageFileList.push({
|
||||
file: fsPath,
|
||||
uuid: uuid,
|
||||
size: this.getFileSize(fsPath)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
if(assetInfo.file && this.isImageFile(assetInfo.file)){
|
||||
imageFileList.push({
|
||||
file: assetInfo.file,
|
||||
uuid: assetInfo.uuid,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return imageFileList;
|
||||
},
|
||||
|
||||
async getMode(){
|
||||
let mode = tools.isX64() ? await Editor.Profile.getConfig(packageCfg.name,'zipMode') || 0 : 1;
|
||||
return mode;
|
||||
},
|
||||
|
||||
async onStartCompressPicture(assetInfo){
|
||||
let imageFileList = await this.getImageFileList(assetInfo);
|
||||
if(imageFileList.length == 0){
|
||||
return console.log("不支持该图片格式的压缩")
|
||||
}
|
||||
let arrList = [];
|
||||
for (let i = 0; i < imageFileList.length; i++) {
|
||||
const fileInfo = imageFileList[i];
|
||||
arrList.push(fileInfo.file);
|
||||
}
|
||||
|
||||
let mode = await this.getMode();
|
||||
if(mode == 0){
|
||||
console.log("---------------------压缩模式:Imagemin---------------------------");
|
||||
imageminApi.compressPicture(arrList,imageFileList);
|
||||
}else{
|
||||
console.log("---------------------压缩模式:TinyPng---------------------------");
|
||||
tinypngApi.compressPicture(arrList,imageFileList);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = AssetsMenu;
|
||||
|
||||
15
extensions/quick-compress-image/src/browser.js
Normal file
15
extensions/quick-compress-image/src/browser.js
Normal file
@@ -0,0 +1,15 @@
|
||||
"use strict";
|
||||
|
||||
exports.methods={
|
||||
'setting'(){
|
||||
Editor.Panel.open('quick-compress-image')
|
||||
}
|
||||
}
|
||||
|
||||
exports.load=function(){
|
||||
|
||||
}
|
||||
|
||||
exports.unload=function(){
|
||||
|
||||
};
|
||||
58
extensions/quick-compress-image/src/setting-panel.js
Normal file
58
extensions/quick-compress-image/src/setting-panel.js
Normal file
@@ -0,0 +1,58 @@
|
||||
let fs = require('fs')
|
||||
let path = require('path')
|
||||
let packageCfg = require('../package.json')
|
||||
const tools = require('./tools/tools')
|
||||
Editor.Panel.define = Editor.Panel.define || ((args)=>{return args})
|
||||
|
||||
|
||||
function saveConfig(doms){
|
||||
Editor.Profile.setConfig(packageCfg.name,'zipRate',doms.zipRateSlider.value);
|
||||
Editor.Profile.setConfig(packageCfg.name,'zipMode',doms.zipModeTab.value);
|
||||
}
|
||||
|
||||
async function getMode(){
|
||||
let mode = tools.isX64() ? await Editor.Profile.getConfig(packageCfg.name,'zipMode') || 0 : 1;
|
||||
return mode;
|
||||
}
|
||||
|
||||
module.exports = Editor.Panel.define({
|
||||
template: fs.readFileSync(path.join(__dirname, '../template/setting.html'), 'utf-8'),
|
||||
style: fs.readFileSync(path.join(__dirname, '../template/setting.css'), 'utf-8'),
|
||||
|
||||
$: {
|
||||
zipRateSlider: '#zipRateSlider',
|
||||
saveBtn: '#saveBtn',
|
||||
zipModeTab: '#zipModeTab',
|
||||
},
|
||||
|
||||
async ready() {
|
||||
this.$.zipRateSlider.value = parseInt(await Editor.Profile.getConfig(packageCfg.name,'zipRate')) || 30;
|
||||
this.$.zipModeTab.value = await getMode();
|
||||
this.$.zipModeTab.value == 1 ? this.$.zipRateSlider.disabled = true : this.$.zipRateSlider.removeAttribute('disabled');
|
||||
this.$.zipModeTab.addEventListener('click',()=>{
|
||||
let mode = this.$.zipModeTab.value;
|
||||
if(!tools.isX64() && mode == 0){
|
||||
this.$.zipModeTab.value = 1;
|
||||
this.$.zipRateSlider.disabled = true
|
||||
alert('CPU不支持该模式');
|
||||
}else{
|
||||
this.$.zipModeTab.value == 1 ? this.$.zipRateSlider.disabled = true : this.$.zipRateSlider.removeAttribute('disabled');
|
||||
saveConfig(this.$);
|
||||
}
|
||||
},0)
|
||||
|
||||
this.$.saveBtn.addEventListener('click',()=>{
|
||||
saveConfig(this.$);
|
||||
},0)
|
||||
},
|
||||
|
||||
|
||||
listeners: {
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
beforeClose() { },
|
||||
close() {
|
||||
saveConfig(this.$);
|
||||
},
|
||||
});
|
||||
20
extensions/quick-compress-image/src/tools/statistical.js
Normal file
20
extensions/quick-compress-image/src/tools/statistical.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const tools = require("./tools")
|
||||
|
||||
let isStatistical = false;
|
||||
module.exports = {
|
||||
|
||||
// 用户使用次数统计
|
||||
countStartupTimes(){
|
||||
if(!isStatistical && Editor.User && Editor.User.getData){
|
||||
Editor.User.getData().then((post_data)=>{
|
||||
if(!post_data) {
|
||||
return
|
||||
}
|
||||
post_data.version = Editor.App ? Editor.App.version : "?";
|
||||
tools.httpPost('120.77.174.207','/compressImageLogincount',8081,post_data);
|
||||
});
|
||||
isStatistical = true;
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
560
extensions/quick-compress-image/src/tools/tools.js
Normal file
560
extensions/quick-compress-image/src/tools/tools.js
Normal file
@@ -0,0 +1,560 @@
|
||||
let fs = require("fs");
|
||||
let path = require("path");
|
||||
let packageCfg = require("../../package.json")
|
||||
var http = require('http');
|
||||
var querystring = require('querystring');
|
||||
var os = require('os');
|
||||
|
||||
const inputType = { "text": 1, "password": 1, "number": 1, "date": 1, "color": 1, "range": 1, "month": 1, "week": 1, "time": 1, "email": 1, "search": 1, "url": 1, "textarea": 1 }
|
||||
let checkFsPath = new RegExp("\\.\\./", "g");
|
||||
let readFileQueue = []
|
||||
let readFileMaxCount = 50
|
||||
let readFileCount = 0
|
||||
|
||||
let translateMap = {}
|
||||
|
||||
module.exports = {
|
||||
|
||||
isX64(){
|
||||
return os.arch() == 'x64'
|
||||
},
|
||||
|
||||
// 不是输入状态是时
|
||||
inputTypeChk(e) {
|
||||
if (e.path[0]) {
|
||||
let type = e.path[0].type;
|
||||
if (inputType[type]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getLanguage(){
|
||||
return window.navigator && window.navigator.language && window.navigator.language.split('-')[0];
|
||||
},
|
||||
|
||||
// 更新i18翻译文本,解决creator不重启不会刷新修改问题
|
||||
initI18t(){
|
||||
let locale = this.getLanguage() || 'zh';
|
||||
let filePath = Editor2D.url('packages://simple-code/i18n/'+locale+'.js');
|
||||
if(!this.isFileExit(filePath)){
|
||||
return
|
||||
}
|
||||
let mapList = require(filePath);
|
||||
let converList = {}
|
||||
for (const key in mapList) {
|
||||
const converText = mapList[key];
|
||||
converList[packageCfg.name+'.'+key] = converText
|
||||
}
|
||||
translateMap = converList;
|
||||
// Editor.i18n.extend(converList)
|
||||
},
|
||||
|
||||
translate(key){
|
||||
let name = packageCfg.name+'.'+key
|
||||
return translateMap[name] || Editor2D.T(name) || name
|
||||
},
|
||||
|
||||
translateZhAndEn(zeText,enText){
|
||||
return this.getLanguage() == 'zh' ? zeText : enText;
|
||||
},
|
||||
|
||||
// 翻译中英文
|
||||
T(zeText,enText){
|
||||
return this.translateZhAndEn(zeText,enText);
|
||||
},
|
||||
|
||||
isEmptyObject(obj){
|
||||
if(obj){
|
||||
for (const key in obj) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
// 拷贝本对象方法到目标对象
|
||||
// newObj 子类
|
||||
// baseObj 父类
|
||||
// mergeFuncs = ["init"]; 新旧类的同名函数合并一起
|
||||
extendTo(newObj, baseObj, mergeFuncs = []) {
|
||||
if (!baseObj || !newObj) return;
|
||||
|
||||
for (let k in baseObj) {
|
||||
let v = baseObj[k]
|
||||
if (newObj[k] == null) {
|
||||
newObj[k] = v
|
||||
}
|
||||
// 函数继承使用 "this._super()" 调用父类
|
||||
else if (typeof v == "function" && typeof newObj[k] == "function" && !newObj[k]._isExend) {
|
||||
let newFunc = newObj[k];
|
||||
newObj[k] = function () {
|
||||
this._super = v;
|
||||
let ret = newFunc.apply(this, arguments);// 执行函数并传入传参
|
||||
delete this._super;
|
||||
return ret;
|
||||
};
|
||||
newObj[k]._isExend = true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
copyToClipboard(str) {
|
||||
var input = str;
|
||||
const el = document.createElement('textarea');
|
||||
el.value = input;
|
||||
el.setAttribute('readonly', '');
|
||||
el.style.contain = 'strict';
|
||||
el.style.position = 'absolute';
|
||||
el.style.left = '-9999px';
|
||||
el.style.fontSize = '12pt'; // Prevent zooming on iOS
|
||||
|
||||
const selection = getSelection();
|
||||
var originalRange = false;
|
||||
if (selection.rangeCount > 0) {
|
||||
originalRange = selection.getRangeAt(0);
|
||||
}
|
||||
document.body.appendChild(el);
|
||||
el.select();
|
||||
el.selectionStart = 0;
|
||||
el.selectionEnd = input.length;
|
||||
|
||||
var success = false;
|
||||
try {
|
||||
success = document.execCommand('copy');
|
||||
} catch (err) { }
|
||||
|
||||
document.body.removeChild(el);
|
||||
|
||||
if (originalRange) {
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(originalRange);
|
||||
}
|
||||
|
||||
return success;
|
||||
},
|
||||
|
||||
parseJson(text){
|
||||
try {
|
||||
return JSON.parse(text)
|
||||
} catch (error) {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
|
||||
objectCount(obj){
|
||||
let len = 0
|
||||
for (const key in obj) {
|
||||
len++
|
||||
}
|
||||
return len;
|
||||
},
|
||||
|
||||
// 获得import路径
|
||||
getImportStringPaths(codeText) {
|
||||
|
||||
var regEx = /(require\(|import |reference path=)(.{0,}['"])(.+)['"]/g;
|
||||
var match = regEx.exec(codeText);
|
||||
var imports = []
|
||||
while (match) {
|
||||
let start = match.index + match[1].length + match[2].length;
|
||||
imports.push({
|
||||
path: match[3],
|
||||
start: start,
|
||||
length: match[3].length,
|
||||
})
|
||||
match = regEx.exec(codeText);
|
||||
}
|
||||
return imports
|
||||
},
|
||||
|
||||
//将相对路径转为绝对路径
|
||||
relativePathTofsPath(absolutePath, relativePath) {
|
||||
var uplayCount = 0; // 相对路径中返回上层的次数。
|
||||
var m = relativePath.match(checkFsPath);
|
||||
if (m) uplayCount = m.length;
|
||||
|
||||
var lastIndex = absolutePath.length - 1;
|
||||
var subString = absolutePath
|
||||
for (var i = 0; i <= uplayCount; i++) {
|
||||
lastIndex = subString.lastIndexOf("/", lastIndex);
|
||||
subString = subString.substr(0, lastIndex)
|
||||
}
|
||||
return this.normPath( subString + "/" + relativePath.substr(relativePath.lastIndexOf('./') + 2));
|
||||
},
|
||||
|
||||
//将绝对路径转为相对路径
|
||||
fsPathToRelativePath(currPath, importPath) {
|
||||
let s_i = currPath.lastIndexOf('/')
|
||||
if (s_i != -1) currPath = currPath.substr(0, s_i);
|
||||
let relativePath = path.relative(currPath, importPath);
|
||||
if (relativePath[0] != '.') {
|
||||
relativePath = './' + relativePath;
|
||||
}
|
||||
return this.normPath(relativePath);
|
||||
},
|
||||
|
||||
|
||||
//转换相对路径
|
||||
converRelative(relativePath, oldFilePath, newFilePath) {
|
||||
let s_i = oldFilePath.lastIndexOf('/')
|
||||
if (s_i != -1) oldFilePath = oldFilePath.substr(0, s_i);
|
||||
s_i = newFilePath.lastIndexOf('/')
|
||||
if (s_i != -1) newFilePath = newFilePath.substr(0, s_i);
|
||||
|
||||
let rve_to_abs = this.normPath(path.resolve(oldFilePath, relativePath));
|
||||
relativePath = this.normPath(path.relative(newFilePath, rve_to_abs));
|
||||
if (relativePath[0] != '.') {
|
||||
relativePath = './' + relativePath;
|
||||
}
|
||||
return relativePath;
|
||||
},
|
||||
|
||||
|
||||
|
||||
normPath(filePath) {
|
||||
return filePath.replace(/\\/g, '/');
|
||||
},
|
||||
|
||||
// 异步读取文件
|
||||
readFileAsyn(filePath,callback)
|
||||
{
|
||||
let args = {filePath,callback};
|
||||
readFileQueue.push(args)
|
||||
// 最多同时读取50个文件
|
||||
readFileCount++;
|
||||
if(readFileCount >= readFileMaxCount){
|
||||
// console.log("readFileAsyn:读取超出最大文件数量",readFileCount);
|
||||
return;
|
||||
}
|
||||
this._handleReadFileQueue()
|
||||
},
|
||||
|
||||
// 处理 readFileAsyn 队列
|
||||
_handleReadFileQueue(){
|
||||
// 1 == Math.min(50,1)
|
||||
let len = Math.min( readFileMaxCount , readFileQueue.length );
|
||||
for (let i = 0; i < len; i++) {
|
||||
// 处理队列
|
||||
let args = readFileQueue.splice(0,1)[0];
|
||||
fs.readFile(args.filePath,(err,data)=>
|
||||
{
|
||||
readFileCount--
|
||||
if(readFileCount > 500){
|
||||
setTimeout(this._handleReadFileQueue.bind(this),100)
|
||||
}else{
|
||||
this._handleReadFileQueue()
|
||||
}
|
||||
|
||||
try {
|
||||
args.callback(err,data);
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
copyFile(sourcePath, toPath) {
|
||||
fs.writeFileSync(toPath, fs.readFileSync(sourcePath))
|
||||
},
|
||||
|
||||
// copyFile(sourcePath,toPath){
|
||||
// fs.readFile(sourcePath,function(err,data){
|
||||
// if(err) throw new Error('复制失败:'+sourcePath+" TO "+data);
|
||||
// fs.writeFile(toPath,data,function(err){
|
||||
// if(err) throw new Error('复制写入失败'+sourcePath+" TO "+data);
|
||||
// })
|
||||
// })
|
||||
// },
|
||||
|
||||
moveDir(sourcePath, toPath) {
|
||||
if (!fs.existsSync(sourcePath)) {
|
||||
console.log("不存在目录:", sourcePath);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sourcePath[sourcePath.length - 1] != path.sep) {
|
||||
sourcePath += path.sep;// 加猴嘴
|
||||
}
|
||||
if (toPath[toPath.length - 1] != path.sep) {
|
||||
toPath += path.sep;// 加猴嘴
|
||||
}
|
||||
|
||||
let list = this.getDirAllFiles(sourcePath, []);
|
||||
list.forEach((fileName, i) => {
|
||||
|
||||
let toFilePath = fileName.replace(sourcePath, toPath);
|
||||
console.log("执行:", fileName, toFilePath);
|
||||
let dirName = path.dirname(toFilePath);
|
||||
this.createDir(dirName);
|
||||
// 移动文件
|
||||
fs.renameSync(fileName, toFilePath);
|
||||
})
|
||||
},
|
||||
|
||||
createDir(dirPath) {
|
||||
if (fs.existsSync(dirPath)) return;
|
||||
let paths = dirPath.split(path.sep);//分割路径
|
||||
let path_ = "";
|
||||
|
||||
// c:\
|
||||
let n = 0
|
||||
let max = paths.length
|
||||
if (paths[0].indexOf(":") != -1) {
|
||||
path_ = paths[0];
|
||||
n++;
|
||||
}
|
||||
|
||||
if (paths[max - 1].indexOf(".") != -1) {
|
||||
max--;
|
||||
}
|
||||
|
||||
for (n; n < max; n++) {
|
||||
path_ += path.sep + paths[n];
|
||||
if (!fs.existsSync(path_)) {
|
||||
fs.mkdirSync(path_);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 获得文件夹列表
|
||||
getDirList(dirPath, result) {
|
||||
let files = fs.readdirSync(dirPath);
|
||||
files.forEach((val, index) => {
|
||||
let fPath = path.join(dirPath, val);
|
||||
if (fs.existsSync(fPath) && fs.statSync(fPath).isDirectory()) {
|
||||
result.push(fPath);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
|
||||
// 获得文件列表
|
||||
getFileList(dirPath, result = []) {
|
||||
let files = fs.readdirSync(dirPath);
|
||||
files.forEach((val, index) => {
|
||||
let fPath = path.join(dirPath, val);
|
||||
if (fs.existsSync(fPath) && fs.statSync(fPath).isFile()) {
|
||||
result.push(fPath);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
|
||||
isDirectory(fPath) {
|
||||
return fs.existsSync(fPath) && fs.statSync(fPath).isDirectory()
|
||||
},
|
||||
|
||||
getDirAllFiles(dirPath, result = []) {
|
||||
let files = fs.readdirSync(dirPath);
|
||||
files.forEach((val, index) => {
|
||||
let fPath = path.join(dirPath, val);
|
||||
if (fs.existsSync(fPath) && fs.statSync(fPath).isDirectory()) {
|
||||
this.getDirAllFiles(fPath, result);
|
||||
} else if (fs.statSync(fPath).isFile()) {
|
||||
result.push(fPath);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
|
||||
getFileString(fileList, options) {
|
||||
let curIndex = 0;
|
||||
let totalIndex = fileList.length;
|
||||
let str = {};
|
||||
for (let key in fileList) {
|
||||
let filePath = fileList[key];
|
||||
let b = this._isFileExit(filePath);
|
||||
if (b) {
|
||||
fs.readFile(filePath, 'utf-8', function (err, data) {
|
||||
if (!err) {
|
||||
self._collectString(data, str);
|
||||
} else {
|
||||
console.log("error: " + filePath);
|
||||
}
|
||||
self._onCollectStep(filePath, ++curIndex, totalIndex, str, options);
|
||||
})
|
||||
} else {
|
||||
self._onCollectStep(filePath, ++curIndex, totalIndex, str, options);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_onCollectStep(filePath, cur, total, str, data) {
|
||||
if (data && data.stepCb) {
|
||||
data.stepCb(filePath, cur, total);
|
||||
}
|
||||
if (cur >= total) {
|
||||
self._onCollectOver(str, data);
|
||||
}
|
||||
},
|
||||
_onCollectOver(collectObjArr, data) {
|
||||
let strArr = [];
|
||||
let str = "";
|
||||
for (let k in collectObjArr) {
|
||||
str += k;
|
||||
strArr.push(k);
|
||||
}
|
||||
// console.log("一共有" + strArr.length + "个字符, " + strArr);
|
||||
console.log("一共有" + strArr.length + "个字符");
|
||||
if (data && data.compCb) {
|
||||
data.compCb(str);
|
||||
}
|
||||
},
|
||||
mkDir(path) {
|
||||
try {
|
||||
fs.mkdirSync(path);
|
||||
} catch (e) {
|
||||
if (e.code !== 'EEXIST') throw e;
|
||||
}
|
||||
},
|
||||
isFileExit(file) {
|
||||
try {
|
||||
fs.accessSync(file, fs.F_OK);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
async isFileExitAsync(file) {
|
||||
return new Promise((resolev)=>{
|
||||
fs.access(file,(err)=>{
|
||||
resolev(err == null);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_collectString(data, collectObject) {
|
||||
for (let i in data) {
|
||||
let char = data.charAt(i);
|
||||
if (collectObject[char]) {
|
||||
collectObject[char]++;
|
||||
} else {
|
||||
collectObject[char] = 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
emptyDir(rootFile) {
|
||||
//删除所有的文件(将所有文件夹置空)
|
||||
let emptyDir = function (fileUrl) {
|
||||
let files = fs.readdirSync(fileUrl);//读取该文件夹
|
||||
for (let k in files) {
|
||||
let filePath = path.join(fileUrl, files[k]);
|
||||
let stats = fs.statSync(filePath);
|
||||
if (stats.isDirectory()) {
|
||||
emptyDir(filePath);
|
||||
} else {
|
||||
fs.unlinkSync(filePath);
|
||||
console.log("删除文件:" + filePath);
|
||||
}
|
||||
}
|
||||
};
|
||||
//删除所有的空文件夹
|
||||
let rmEmptyDir = function (fileUrl) {
|
||||
let files = fs.readdirSync(fileUrl);
|
||||
if (files.length > 0) {
|
||||
for (let k in files) {
|
||||
let rmDir = path.join(fileUrl, files[k]);
|
||||
rmEmptyDir(rmDir);
|
||||
}
|
||||
if (fileUrl !== rootFile) {// 不删除根目录
|
||||
fs.rmdirSync(fileUrl);
|
||||
console.log('删除空文件夹' + fileUrl);
|
||||
}
|
||||
} else {
|
||||
if (fileUrl !== rootFile) {// 不删除根目录
|
||||
fs.rmdirSync(fileUrl);
|
||||
console.log('删除空文件夹' + fileUrl);
|
||||
}
|
||||
}
|
||||
};
|
||||
emptyDir(rootFile);
|
||||
rmEmptyDir(rootFile);
|
||||
},
|
||||
/*
|
||||
is_fileType($('#uploadfile').val(), 'doc,pdf,txt,wps,odf,md,png,gif,jpg')
|
||||
* */
|
||||
is_fileType(filename, types) {
|
||||
types = types.split(',');
|
||||
let pattern = '\.(';
|
||||
for (let i = 0; i < types.length; i++) {
|
||||
if (0 !== i) {
|
||||
pattern += '|';
|
||||
}
|
||||
pattern += types[i].trim();
|
||||
}
|
||||
pattern += ')$';
|
||||
return new RegExp(pattern, 'i').test(filename);
|
||||
},
|
||||
|
||||
getFileName(filePath) {
|
||||
let s_i = filePath.lastIndexOf('/');
|
||||
if (s_i == -1) s_i = filePath.lastIndexOf('\\');
|
||||
let name = filePath
|
||||
if (s_i != -1) name = name.substr(s_i + 1)
|
||||
s_i = name.lastIndexOf('.');
|
||||
if (s_i != -1) {
|
||||
name = name.substr(0, s_i)
|
||||
}
|
||||
return name;
|
||||
},
|
||||
|
||||
getFileExtname(filePath) {
|
||||
let s_i = filePath.lastIndexOf('.');
|
||||
let extname = ""
|
||||
if (s_i != -1) {
|
||||
extname = filePath.substr(s_i).toLowerCase()
|
||||
}
|
||||
return extname;
|
||||
},
|
||||
|
||||
|
||||
getUrlInfo(url) {
|
||||
let s_i = url.lastIndexOf('/');
|
||||
if (s_i == -1) s_i = url.lastIndexOf('\\');
|
||||
|
||||
let name = ""
|
||||
if (s_i != -1) name = url.substr(s_i + 1)
|
||||
|
||||
s_i = name.lastIndexOf('.');
|
||||
let extname = ""
|
||||
if (s_i != -1) {
|
||||
extname = name.substr(s_i).toLowerCase()
|
||||
}
|
||||
return { name, extname, url }
|
||||
},
|
||||
|
||||
|
||||
httpPost(ip,path,port,args,callback){
|
||||
var options = {
|
||||
hostname: ip,
|
||||
port: port,
|
||||
path: path,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
||||
}
|
||||
};
|
||||
|
||||
var req = http.request(options, function (res) {
|
||||
// console.log('STATUS: ' + res.statusCode);
|
||||
// console.log('HEADERS: ' + JSON.stringify(res.headers));
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', function (chunk) {
|
||||
if(callback) callback(chunk);
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', function (e) {
|
||||
if(callback) callback();
|
||||
});
|
||||
|
||||
// write data to request body
|
||||
var content = querystring.stringify(args);
|
||||
req.write(content);
|
||||
req.end();
|
||||
},
|
||||
|
||||
}
|
||||
136
extensions/quick-compress-image/src/tools/updater.js
Normal file
136
extensions/quick-compress-image/src/tools/updater.js
Normal file
@@ -0,0 +1,136 @@
|
||||
const fetch = require('./node-fetch');
|
||||
|
||||
/**
|
||||
* 更新器
|
||||
*/
|
||||
const Updater = {
|
||||
|
||||
/**
|
||||
* 远端地址
|
||||
* @type {string}
|
||||
*/
|
||||
remoteUrl: 'https://gitee.com/mcv/simple-code-describe',
|
||||
|
||||
/**
|
||||
* 获取远端的 package.json
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
async getRemotePackageJson() {
|
||||
const packageJsonUrl = `${this.remoteUrl}/raw/master/package3.json`;
|
||||
// 发起网络请求
|
||||
const response = await fetch(packageJsonUrl, {
|
||||
method: 'GET',
|
||||
cache: 'no-cache',
|
||||
mode: 'no-cors',
|
||||
});
|
||||
// 请求结果
|
||||
if (response.status !== 200) {
|
||||
return null;
|
||||
}
|
||||
// 读取 json
|
||||
const json = response.json();
|
||||
return json;
|
||||
},
|
||||
|
||||
async getRemoteDesc() {
|
||||
const descUrl = `${this.remoteUrl}/raw/master/README3.md`;
|
||||
// 发起网络请求
|
||||
const response = await fetch(descUrl, {
|
||||
method: 'GET',
|
||||
cache: 'no-cache',
|
||||
mode: 'no-cors',
|
||||
});
|
||||
// 请求结果
|
||||
if (response.status !== 200) {
|
||||
return '';
|
||||
}
|
||||
const text = response.text();
|
||||
return text;
|
||||
},
|
||||
/**
|
||||
* 获取远端版本号
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async getRemoteVersion() {
|
||||
const package = await this.getRemotePackageJson();
|
||||
if (!package) {
|
||||
return null;
|
||||
}
|
||||
const version = package.version || null;
|
||||
return version;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取本地版本号
|
||||
* @returns {string}
|
||||
*/
|
||||
getLocalVersion() {
|
||||
return require('../package.json').version;
|
||||
},
|
||||
|
||||
/**
|
||||
* 拆分版本号
|
||||
* @param {string} version 版本号文本
|
||||
* @returns {number[]}
|
||||
* @example
|
||||
* splitVersionString('1.2.0'); // [1, 2, 0]
|
||||
*/
|
||||
splitVersionString(version) {
|
||||
return (
|
||||
version.replace(/-/g, '.')
|
||||
.split('.')
|
||||
.map(v => (parseInt(v) || 0))
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* 对比版本号
|
||||
* @param {string} a 版本 a
|
||||
* @param {string} b 版本 b
|
||||
* @returns {-1 | 0 | 1}
|
||||
* @example
|
||||
* compareVersion('1.0.0', '1.0.1'); // -1
|
||||
* compareVersion('1.1.0', '1.1.0'); // 0
|
||||
* compareVersion('1.2.1', '1.2.0'); // 1
|
||||
* compareVersion('1.2.0.1', '1.2.0'); // 1
|
||||
*/
|
||||
compareVersion(a, b) {
|
||||
const acs = this.splitVersionString(a),
|
||||
bcs = this.splitVersionString(b);
|
||||
const count = Math.max(acs.length, bcs.length);
|
||||
for (let i = 0; i < count; i++) {
|
||||
const ac = acs[i],
|
||||
bc = bcs[i];
|
||||
// 前者缺少分量或前者小于后者
|
||||
if (ac == null || ac < bc) {
|
||||
return -1;
|
||||
}
|
||||
// 后者缺少分量或前者大于后者
|
||||
if (bc == null || ac > bc) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查远端是否有新版本
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async check() {
|
||||
// 远端版本号
|
||||
const remoteVersion = await this.getRemoteVersion();
|
||||
if (!remoteVersion) {
|
||||
return '';
|
||||
}
|
||||
// 本地版本号
|
||||
const localVersion = this.getLocalVersion();
|
||||
// 对比版本号
|
||||
const result = this.compareVersion(localVersion, remoteVersion);
|
||||
const remoteDesc = await this.getRemoteDesc() || '';
|
||||
return (result < 0) ? remoteDesc : '';
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = Updater;
|
||||
214
extensions/quick-compress-image/src/tools/watchFile.js
Normal file
214
extensions/quick-compress-image/src/tools/watchFile.js
Normal file
@@ -0,0 +1,214 @@
|
||||
/**
|
||||
* 1.监听文件变动
|
||||
*/
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const tools = require("./tools");
|
||||
|
||||
|
||||
/**
|
||||
* 事件回调
|
||||
*
|
||||
* @callback WatchEventCallback
|
||||
* @param {string} eventName - 事件名 create | delete | change | init
|
||||
* @param {Array<string>} files - 改变的文件
|
||||
*/
|
||||
|
||||
/**
|
||||
* 监听事件
|
||||
*/
|
||||
class WatchFile {
|
||||
/**
|
||||
* @param {WatchFile} watchObj
|
||||
* @param {string} pathName
|
||||
* @param {WatchEventCallback} eventCallback
|
||||
*/
|
||||
constructor(watchObj,pathName,eventCallback){
|
||||
this.pathName = pathName;
|
||||
this.eventCallback = eventCallback;
|
||||
this._watchObj = watchObj;
|
||||
/** @type Object<string,fs.Stats> */
|
||||
this._files = {}
|
||||
this._isChecking = false;
|
||||
this._isInit = false;
|
||||
this._isExistPath = false;
|
||||
this.check();
|
||||
}
|
||||
|
||||
stop(){
|
||||
this._watchObj.removeWatch(this.eventCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新文件状态
|
||||
*/
|
||||
async check(){
|
||||
if(this._isChecking){
|
||||
return;
|
||||
}
|
||||
let pathStat = fs.existsSync(this.pathName) ? fs.statSync(this.pathName) : undefined;
|
||||
if(!pathStat){
|
||||
// 监听的目录被删除
|
||||
if(this._isExistPath) setTimeout(this._removeAllFile.bind(this),1);
|
||||
this._isExistPath = false;
|
||||
return ;
|
||||
}
|
||||
|
||||
this._isExistPath = true;
|
||||
this._isChecking = true;
|
||||
let newFiles = {}
|
||||
if(pathStat && pathStat.isDirectory())
|
||||
{
|
||||
newFiles = await getDirAllFiles(this.pathName,newFiles)
|
||||
this._update(newFiles)
|
||||
}else if(pathStat.isFile())
|
||||
{
|
||||
newFiles[this.pathName] = pathStat;
|
||||
// 延迟执行防止循环调用
|
||||
setTimeout(()=>this._update(newFiles),1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新状态
|
||||
* @param {Object<string,fs.Stats>} newFiles
|
||||
*/
|
||||
_update(newFiles){
|
||||
let deleteFiles = [];
|
||||
let changeFiles = [];
|
||||
let createFiles = [];
|
||||
for (const filePath in this._files) {
|
||||
const oldFileStats = this._files[filePath];
|
||||
const newFileStats = newFiles[filePath]
|
||||
if(newFileStats == null){
|
||||
// 文件被删
|
||||
deleteFiles.push(filePath);
|
||||
}else if(newFileStats.mtimeMs != oldFileStats.mtimeMs){
|
||||
// 文件被修改
|
||||
changeFiles.push(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
for (const filePath in newFiles) {
|
||||
/** @type fs.Stats */
|
||||
const oldFileStats = this._files[filePath];
|
||||
if(oldFileStats == null){
|
||||
// 新创文件
|
||||
createFiles.push(filePath);
|
||||
}
|
||||
}
|
||||
let isInit = this._isInit;
|
||||
this._files = newFiles;
|
||||
this._isChecking = false;
|
||||
this._isInit = true;
|
||||
|
||||
// 调用事件
|
||||
if(this.eventCallback){
|
||||
if(!isInit){
|
||||
this.eventCallback('init',createFiles);
|
||||
}else{
|
||||
if(deleteFiles.length) this.eventCallback('delete',deleteFiles);
|
||||
if(changeFiles.length) this.eventCallback('change',changeFiles);
|
||||
if(createFiles.length) this.eventCallback('create',createFiles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_removeAllFile(){
|
||||
let deleteFiles = [];
|
||||
for (const filePath in this._files) {
|
||||
deleteFiles.push(filePath);
|
||||
}
|
||||
this._files = {};
|
||||
if(deleteFiles.length) this.eventCallback('delete',deleteFiles);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听文件类
|
||||
*/
|
||||
class WatchMgr{
|
||||
constructor(){
|
||||
/** @type [WatchFile] */
|
||||
this.eventListens = []
|
||||
this.watchFileBuffs = {}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} pathName
|
||||
* @param {WatchEventCallback} eventCallback
|
||||
* @returns {WatchFile}
|
||||
*/
|
||||
addWatchPath(pathName,eventCallback) {
|
||||
let watchFile = new WatchFile(this,pathName,eventCallback);
|
||||
this.eventListens.push(watchFile);
|
||||
return watchFile;
|
||||
}
|
||||
|
||||
checkAll() {
|
||||
for (let i = 0; i < this.eventListens.length; i++) {
|
||||
const watchFile = this.eventListens[i];
|
||||
watchFile.check();
|
||||
}
|
||||
}
|
||||
|
||||
check(pathName) {
|
||||
for (let i = 0; i < this.eventListens.length; i++) {
|
||||
const watchFile = this.eventListens[i];
|
||||
if(watchFile.pathName == pathName){
|
||||
watchFile.check();
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removeWatch(pathName) {
|
||||
for (let i = 0; i < this.eventListens.length; i++) {
|
||||
const watchFile = this.eventListens[i];
|
||||
if(watchFile.pathName == pathName){
|
||||
this.eventListens.splice(i,1);
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function getDirAllFiles(dirPath ,result = {}) {
|
||||
return new Promise((resolve, reject )=>
|
||||
{
|
||||
fs.readdir(dirPath,(err,files)=>
|
||||
{
|
||||
if(err) return reject(err);
|
||||
let len = files.length;
|
||||
if(len == 0){
|
||||
resolve(result); // 没有文件
|
||||
return;
|
||||
}
|
||||
|
||||
let cur_ind = 0;
|
||||
files.forEach((val) => {
|
||||
let fPath = path.join(dirPath, val);
|
||||
fs.stat(fPath,async (err,stat)=>
|
||||
{
|
||||
if(err) return reject(err);
|
||||
if (stat.isDirectory()) {
|
||||
result = await getDirAllFiles(fPath,result);
|
||||
} else if (stat.isFile()) {
|
||||
result[fPath] = stat ;
|
||||
}
|
||||
cur_ind ++;
|
||||
if(cur_ind == len) {
|
||||
resolve(result)
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
exports.WatchMgr = WatchMgr;
|
||||
exports.WatchFile = WatchFile;
|
||||
21
extensions/quick-compress-image/template/setting.css
Normal file
21
extensions/quick-compress-image/template/setting.css
Normal file
@@ -0,0 +1,21 @@
|
||||
#box {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
flex-direction: column;
|
||||
align-content: flex-start;
|
||||
align-items: stretch;
|
||||
justify-content: space-between;
|
||||
height: -webkit-fill-available;
|
||||
|
||||
margin-left: 22px;
|
||||
margin-right: 22px;
|
||||
margin-top: 22px;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.layoutItem {
|
||||
flex: 0.5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 0;
|
||||
}
|
||||
15
extensions/quick-compress-image/template/setting.html
Normal file
15
extensions/quick-compress-image/template/setting.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<div id="box">
|
||||
<div class="layoutItem">
|
||||
压缩模式 :<br/>
|
||||
<ui-tab id = 'zipModeTab' value="0">
|
||||
<ui-button>Imagemin(离线)</ui-button>
|
||||
<ui-button>TinyPng(在线)</ui-button>
|
||||
</ui-tab>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="layoutItem">
|
||||
压缩值 (越小压缩率越大):
|
||||
<ui-slider id = 'zipRateSlider' step="1" value="70" min="1" max="100"></ui-slider>
|
||||
</div>
|
||||
<ui-button class="layoutItem" id = 'saveBtn' class="green">保存</ui-button>
|
||||
</div>
|
||||
1342
extensions/quick-compress-image/type/editor.d.ts
vendored
Normal file
1342
extensions/quick-compress-image/type/editor.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user