提交 c27580df authored 作者: 王鹏飞's avatar 王鹏飞

feat(reader): add reader API, bridge, and content handling

- Implemented readerApi for handling POST requests to the reader service. - Created readerBridge for communication with Flutter, including methods to call handlers and open webviews. - Defined READER_ENDPOINTS for various API endpoints related to book information and user interactions. - Added functions to normalize offline payloads and check for valid offline data structures. - Updated Vite configuration to improve project structure and build process, including dynamic HTML entry points and output settings.
上级 843e729c
...@@ -13,12 +13,11 @@ ...@@ -13,12 +13,11 @@
**参数格式**: 逗号分隔的字符串 **参数格式**: 逗号分隔的字符串
```javascript ```javascript
callbackInFlutterComponent('baseURL,book_id,chapter_id,token,position') callbackInFlutterComponent('book_id,chapter_id,token,position')
``` ```
**参数说明**: **参数说明**:
- `baseURL` (string): API 基础地址
- `book_id` (string): 书籍 ID,例如: "175" - `book_id` (string): 书籍 ID,例如: "175"
- `chapter_id` (string): 章节 ID,例如: "335" - `chapter_id` (string): 章节 ID,例如: "335"
- `token` (string): 用户认证令牌 - `token` (string): 用户认证令牌
...@@ -27,7 +26,7 @@ callbackInFlutterComponent('baseURL,book_id,chapter_id,token,position') ...@@ -27,7 +26,7 @@ callbackInFlutterComponent('baseURL,book_id,chapter_id,token,position')
**示例**: **示例**:
```javascript ```javascript
callbackInFlutterComponent('https://api.example.com,175,335,726cca8efcb328672202656f63e176e8,bgi0jn6p') callbackInFlutterComponent('175,335,726cca8efcb328672202656f63e176e8,bgi0jn6p')
``` ```
--- ---
...@@ -37,12 +36,11 @@ callbackInFlutterComponent('https://api.example.com,175,335,726cca8efcb328672202 ...@@ -37,12 +36,11 @@ callbackInFlutterComponent('https://api.example.com,175,335,726cca8efcb328672202
**参数格式**: 逗号分隔的字符串 **参数格式**: 逗号分隔的字符串
```javascript ```javascript
callbackInFlutterComponent('baseURL,book_id,chapter_id,token,position') callbackInFlutterComponent('book_id,chapter_id,token,position')
``` ```
**参数说明**: **参数说明**:
- `baseURL` (string): API 基础地址
- `book_id` (string): 书籍 ID,例如: "323" - `book_id` (string): 书籍 ID,例如: "323"
- `chapter_id` (string): 章节 ID,例如: "2635" - `chapter_id` (string): 章节 ID,例如: "2635"
- `token` (string): 用户认证令牌 - `token` (string): 用户认证令牌
...@@ -51,7 +49,7 @@ callbackInFlutterComponent('baseURL,book_id,chapter_id,token,position') ...@@ -51,7 +49,7 @@ callbackInFlutterComponent('baseURL,book_id,chapter_id,token,position')
**示例**: **示例**:
```javascript ```javascript
callbackInFlutterComponent('https://api.example.com,175,2834,726cca8efcb328672202656f63e176e8,bgi0jn6p') callbackInFlutterComponent('175,2834,726cca8efcb328672202656f63e176e8,bgi0jn6p')
``` ```
--- ---
...@@ -61,19 +59,18 @@ callbackInFlutterComponent('https://api.example.com,175,2834,726cca8efcb32867220 ...@@ -61,19 +59,18 @@ callbackInFlutterComponent('https://api.example.com,175,2834,726cca8efcb32867220
**参数格式**: 逗号分隔的字符串 **参数格式**: 逗号分隔的字符串
```javascript ```javascript
callbackInFlutterComponent('baseURL,book_id,token') callbackInFlutterComponent('book_id,token')
``` ```
**参数说明**: **参数说明**:
- `baseURL` (string): API 基础地址
- `book_id` (string): 书籍 ID - `book_id` (string): 书籍 ID
- `token` (string): 用户认证令牌 - `token` (string): 用户认证令牌
**示例**: **示例**:
```javascript ```javascript
callbackInFlutterComponent('https://api.example.com,175,726cca8efcb328672202656f63e176e8') callbackInFlutterComponent('175,726cca8efcb328672202656f63e176e8')
``` ```
--- ---
...@@ -83,12 +80,11 @@ callbackInFlutterComponent('https://api.example.com,175,726cca8efcb328672202656f ...@@ -83,12 +80,11 @@ callbackInFlutterComponent('https://api.example.com,175,726cca8efcb328672202656f
**参数格式**: 逗号分隔的字符串 **参数格式**: 逗号分隔的字符串
```javascript ```javascript
callbackInFlutterComponent('baseURL,book_id,chapter_id,token,noteId,searchText') callbackInFlutterComponent('book_id,chapter_id,token,noteId,searchText')
``` ```
**参数说明**: **参数说明**:
- `baseURL` (string): API 基础地址
- `book_id` (string): 书籍 ID,例如: "175" 或 "358" - `book_id` (string): 书籍 ID,例如: "175" 或 "358"
- `chapter_id` (string): 章节 ID,例如: "335" 或 "1931" - `chapter_id` (string): 章节 ID,例如: "335" 或 "1931"
- `token` (string): 用户认证令牌 - `token` (string): 用户认证令牌
...@@ -98,46 +94,17 @@ callbackInFlutterComponent('baseURL,book_id,chapter_id,token,noteId,searchText') ...@@ -98,46 +94,17 @@ callbackInFlutterComponent('baseURL,book_id,chapter_id,token,noteId,searchText')
**示例**: **示例**:
```javascript ```javascript
callbackInFlutterComponent('https://api.example.com,175,335,2f85d063f94485c0c2ef31e652875bd5,,') callbackInFlutterComponent('175,335,2f85d063f94485c0c2ef31e652875bd5,,')
``` ```
--- ---
### 5. read_unline.html (在线阅读页面) ### 5. read_info.html (阅读信息页面)
**参数格式**: 对象形式 **参数格式**: 对象形式
```javascript ```javascript
callbackInFlutterComponent({ callbackInFlutterComponent({
book_id: '175',
chapter_id: '335',
next_chapter: {...}, // 下一章节信息
up_chapter: {...}, // 上一章节信息
color_line: {...}, // 高亮信息
direction: 'scrollUp' // 可选,滚动方向
})
```
**参数说明**:
- `book_id` (string): 书籍 ID
- `chapter_id` (string): 章节 ID
- `next_chapter` (object, 可选): 下一章节信息对象
- `up_chapter` (object, 可选): 上一章节信息对象
- `color_line` (object, 可选): 高亮线条信息
- `direction` (string, 可选): 滚动方向,例如: "scrollUp"
**注意**: 此页面会将参数对象直接传递给 `getBookChapterData` 方法处理。
---
### 6. read_info.html (阅读信息页面)
**参数格式**: 对象形式
```javascript
callbackInFlutterComponent({
base_url: 'https://api.example.com',
book_id: '175', book_id: '175',
chapter_id: '2834', chapter_id: '2834',
token: '726cca8efcb328672202656f63e176e8', token: '726cca8efcb328672202656f63e176e8',
...@@ -148,7 +115,6 @@ callbackInFlutterComponent({ ...@@ -148,7 +115,6 @@ callbackInFlutterComponent({
**参数说明**: **参数说明**:
- `base_url` (string): API 基础地址
- `book_id` (string): 书籍 ID,例如: "175" 或 "358" - `book_id` (string): 书籍 ID,例如: "175" 或 "358"
- `chapter_id` (string): 章节 ID,例如: "2253" 或 "2825" - `chapter_id` (string): 章节 ID,例如: "2253" 或 "2825"
- `token` (string): 用户认证令牌 - `token` (string): 用户认证令牌
...@@ -159,7 +125,6 @@ callbackInFlutterComponent({ ...@@ -159,7 +125,6 @@ callbackInFlutterComponent({
```javascript ```javascript
callbackInFlutterComponent({ callbackInFlutterComponent({
base_url: 'https://api.example.com',
book_id: 175, book_id: 175,
chapter_id: 2834, chapter_id: 2834,
token: '726cca8efcb328672202656f63e176e8', token: '726cca8efcb328672202656f63e176e8',
...@@ -170,7 +135,7 @@ callbackInFlutterComponent({ ...@@ -170,7 +135,7 @@ callbackInFlutterComponent({
--- ---
### 7. read_img.html (阅读图片页面) ### 6. read_img.html (阅读图片页面)
**参数格式**: 对象形式 **参数格式**: 对象形式
......
...@@ -40,12 +40,15 @@ pnpm preview ...@@ -40,12 +40,15 @@ pnpm preview
## 项目结构 ## 项目结构
- `*.html` - 多页面入口文件 - `src/*.html` - 多页面入口文件
- `src/reader/` - 阅读器运行时代码
- `assets/` - 静态资源目录 - `assets/` - 静态资源目录
- `css/` - 样式文件 - `css/` - 样式文件
- `js/` - JavaScript 文件
- `images/` - 图片资源 - `images/` - 图片资源
- `fonts/` - 字体文件 - `fonts/` - 字体文件
- `public/` - 构建时原样复制的静态文件
- `assets/js/` - 传统 JavaScript 依赖
- `agreement/` - 协议页面
## 页面说明 ## 页面说明
...@@ -53,9 +56,14 @@ pnpm preview ...@@ -53,9 +56,14 @@ pnpm preview
- `evaluating_result.html` - 测评结果页面 - `evaluating_result.html` - 测评结果页面
- `evaluating_wrong.html` - 错题页面 - `evaluating_wrong.html` - 错题页面
- `read.html` - 阅读页面 - `read.html` - 阅读页面
- `read_unline.html` - 在线阅读页面
- `read_info.html` - 阅读信息页面 - `read_info.html` - 阅读信息页面
- `read_img.html` - 阅读图片页面 - `read_img.html` - 阅读图片页面
## 阅读器结构
`read.html` 是统一阅读器入口,在线章节和离线章节都通过
`callbackInFlutterComponent()` 进入同一套渲染逻辑。
阅读器运行时位于 `src/reader/`,构建后通过 `dist/.vite/manifest.json`
和带 hash 的资源文件支持版本化发布。传统依赖位于 `public/assets/js/`,
会原样复制到构建产物。
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
``` ```
Flutter 调用 callbackInFlutterComponent() Flutter 调用 callbackInFlutterComponent()
↓ ↓
解析参数 (baseURL, book_id, chapter_id, token, noteId, searchText) 解析参数 (book_id, chapter_id, token, noteId, searchText)
↓ ↓
存储 token 到 localStorage 存储 token 到 localStorage
↓ ↓
...@@ -72,7 +72,7 @@ progress = (已滚动高度 + 屏幕高度) / 总内容高度 * 100 ...@@ -72,7 +72,7 @@ progress = (已滚动高度 + 屏幕高度) / 总内容高度 * 100
#### `callbackInFlutterComponent(args)` #### `callbackInFlutterComponent(args)`
**参数格式**: 逗号分隔的字符串 **参数格式**: 逗号分隔的字符串
``` ```
baseURL,book_id,chapter_id,token,noteId,searchText book_id,chapter_id,token,noteId,searchText
``` ```
**功能**: 初始化页面,加载章节数据 **功能**: 初始化页面,加载章节数据
...@@ -264,4 +264,3 @@ refreshTokenSuccess({ token: 'new_token' }) ...@@ -264,4 +264,3 @@ refreshTokenSuccess({ token: 'new_token' })
2. **Token 管理**: Token 会自动存储到 `localStorage` 2. **Token 管理**: Token 会自动存储到 `localStorage`
3. **离线支持**: 笔记操作支持离线,通过回调同步到 Flutter 3. **离线支持**: 笔记操作支持离线,通过回调同步到 Flutter
4. **触摸事件**: 移动端使用触摸事件,桌面端可能需要适配 4. **触摸事件**: 移动端使用触摸事件,桌面端可能需要适配
...@@ -142,39 +142,9 @@ light.c-ff4500::selection { ...@@ -142,39 +142,9 @@ light.c-ff4500::selection {
width: 100vw; width: 100vw;
overflow-x: hidden; overflow-x: hidden;
} }
.chapter-container .load-chapter {
position: fixed;
width: 100vw;
height: 1.3333333333333333rem;
z-index: -1;
background-color: #f2f2f2;
opacity: 0;
}
.chapter-container .load-chapter.next-chapter {
bottom: 0;
left: 0;
}
.chapter-container .load-chapter.prev-chapter {
top: 0;
left: 0;
}
.chapter-container .load-chapter.side {
left: 100vw;
top: 0;
}
.chapter-container .load-chapter .chapter-c-title {
font-size: 0.37333333333333335rem;
padding: 0.13333333333333333rem 0.4rem;
box-sizing: border-box;
line-height: 1.0666666666666667rem;
}
.chapter-container .load-chapter .chapter-c-title strong {
font-weight: 600;
}
.chapter-container .chapter-content-list { .chapter-container .chapter-content-list {
max-height: 100vh;
min-height: 100vh; min-height: 100vh;
overflow: hidden; overflow: visible;
position: relative; position: relative;
} }
.chapter-container .chapter-content-list .show-up { .chapter-container .chapter-content-list .show-up {
...@@ -185,11 +155,7 @@ light.c-ff4500::selection { ...@@ -185,11 +155,7 @@ light.c-ff4500::selection {
top: -100%; top: -100%;
} }
.chapter-container .chapter-content-list .show { .chapter-container .chapter-content-list .show {
-webkit-transform: translateY(0);
transform: translateY(0);
position: relative; position: relative;
transition-timing-function: cubic-bezier(0.1, 0.57, 0.1, 1);
transition-duration: 0ms;
min-height: 100vh; min-height: 100vh;
} }
.chapter-container .chapter-content-list .chapter-item, .chapter-container .chapter-content-list .chapter-item,
...@@ -1230,3 +1196,16 @@ light.c-ff4500::selection { ...@@ -1230,3 +1196,16 @@ light.c-ff4500::selection {
left: 0; left: 0;
width: 100vw; width: 100vw;
} }
.reader-loading {
position: fixed;
top: 12px;
left: 50%;
z-index: 20;
transform: translateX(-50%);
padding: 6px 12px;
border-radius: 14px;
background: rgba(0, 0, 0, 0.55);
color: #fff;
font-size: 12px;
pointer-events: none;
}
"use strict";
var sortObjASCII = function sortObjASCII(obj) {
var arr = Object.keys(obj).sort();
if (arr.length === 0) {
return {};
}
var sortObj = {};
for (var ii in arr) {
sortObj[arr[ii]] = obj[arr[ii]];
}
return sortObj;
};
function getToken() {
return window.localStorage.getItem('token') ? window.localStorage.getItem('token') : '';
}
var request = axios.create({
timeout: 120000
}); // 请求拦截器
request.interceptors.request.use(function (config) {
var token = getToken(); // 请求头带上token
if (token) {
config.headers.Authorization = token;
} else {
config.headers.Authorization = "075895df18c38c8950999cef1af1f0d6";
} // 用户编码
config.headers.UserNo = window.localStorage.getItem('user_no'); // 应用设置
var appId = 'dkFv6OwWyQhSeaZG';
var appSecret = 'd34cea92316d1da113e95158bed9ca97';
config.headers.AppId = appId;
config.headers.AppSecret = appSecret; // 签名
var signData = Qs.parse(config.data);
var timeStr = Date.parse(new Date()) / 1000;
signData.appId = appId;
signData.appSecret = appSecret;
signData.timestamp = timeStr;
signData.url = decodeURI(config.url);
signData.token = token || config.headers.Authorization; // 按照ASCII码进行排序
var dataSign = sortObjASCII(signData); // 转换为字符串
var signStr = '';
for (var key in dataSign) {
var str = '';
if (typeof signData[key] !== 'string' && signData[key].constructor !== String) {
str = JSON.stringify(signData[key]);
} else {
str = signData[key];
}
if (signStr === '') {
signStr = key + '=' + str;
} else {
signStr += '&' + key + '=' + str;
}
}
var salt = '&4F6g4Y6b5L4R9';
var sign = signStr + salt;
config.headers.Sign = md5(md5(sign)); // 加密盐
config.headers.Timestamp = timeStr; // 请求时间
return config;
}, function (error) {
return error;
return Promise.reject(error);
}); // 响应拦截器
request.interceptors.response.use(function (response) {
if (response.status === 200) {
if (response.data && response.data.code === 200) {
return response.data;
} else {
return response;
}
} else {
return response;
}
return false;
}, function (error) {
var str = "message: ".concat(error.message, " \n data: ").concat(error.config.data, " \n token: ").concat(error.config.headers.Authorization);
return error;
return Promise.reject(error);
});
\ No newline at end of file
import fs from 'node:fs'
import path from 'node:path'
import chalk from 'chalk'
import OSS from 'ali-oss'
const log = console.log
const __dirname = path.resolve()
const client = new OSS({
region: 'oss-cn-beijing',
accessKeyId: 'LTAI5t6yKc2m47ZUZTBbUBuP',
accessKeySecret: 'mhuPVC43NAkzBaheHyWGjREiIrLax0',
bucket: 'webapp-pub',
})
async function uploadTarget(src, dist) {
try {
const result = await client.put(dist, path.join(__dirname, src))
log(chalk.green('上传成功', result.url))
} catch (e) {
log(chalk.red('上传失败', src))
log(e)
}
}
function generateUploadTarget(src, dist) {
fs.readdir(path.join(__dirname, src), function (err, files) {
if (err) {
log(err)
return
}
files.forEach(function (file) {
const _src = src + '/' + file
const _dist = dist + '/' + file
const stats = fs.statSync(path.join(__dirname, _src))
// 判断是否为文件
stats.isFile() && uploadTarget(_src, _dist)
// 判断是否为文件夹
stats.isDirectory() && generateUploadTarget(_src, _dist)
})
})
}
generateUploadTarget('./dist', '/book-app')
...@@ -5,10 +5,14 @@ ...@@ -5,10 +5,14 @@
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",
"preview": "vite preview" "preview": "vite preview",
"deploy": "node deploy.js",
"release": "vite build && node deploy.js"
}, },
"devDependencies": { "devDependencies": {
"vite": "^7.3.0", "ali-oss": "^6.23.0",
"glob": "^11.0.0" "chalk": "^6.0.0",
"glob": "^11.0.0",
"vite": "^8.2.1"
} }
} }
...@@ -8,12 +8,18 @@ importers: ...@@ -8,12 +8,18 @@ importers:
.: .:
devDependencies: devDependencies:
ali-oss:
specifier: ^6.23.0
version: 6.23.0
chalk:
specifier: ^6.0.0
version: 6.0.0
glob: glob:
specifier: ^11.0.0 specifier: ^11.0.0
version: 11.1.0 version: 11.1.0
vite: vite:
specifier: ^7.3.0 specifier: ^8.2.1
version: 7.3.0 version: 8.2.1(esbuild@0.27.2)
packages: packages:
...@@ -185,118 +191,113 @@ packages: ...@@ -185,118 +191,113 @@ packages:
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'} engines: {node: '>=12'}
'@rollup/rollup-android-arm-eabi@4.54.0': '@oxc-project/types@0.143.0':
resolution: {integrity: sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==} resolution: {integrity: sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==}
cpu: [arm]
os: [android]
'@rollup/rollup-android-arm64@4.54.0': '@rolldown/binding-android-arm64@1.2.3':
resolution: {integrity: sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==} resolution: {integrity: sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [android] os: [android]
'@rollup/rollup-darwin-arm64@4.54.0': '@rolldown/binding-darwin-arm64@1.2.3':
resolution: {integrity: sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==} resolution: {integrity: sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@rollup/rollup-darwin-x64@4.54.0': '@rolldown/binding-darwin-x64@1.2.3':
resolution: {integrity: sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==} resolution: {integrity: sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@rollup/rollup-freebsd-arm64@4.54.0': '@rolldown/binding-freebsd-x64@1.2.3':
resolution: {integrity: sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==} resolution: {integrity: sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg==}
cpu: [arm64] engines: {node: ^20.19.0 || >=22.12.0}
os: [freebsd]
'@rollup/rollup-freebsd-x64@4.54.0':
resolution: {integrity: sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==}
cpu: [x64] cpu: [x64]
os: [freebsd] os: [freebsd]
'@rollup/rollup-linux-arm-gnueabihf@4.54.0': '@rolldown/binding-linux-arm-gnueabihf@1.2.3':
resolution: {integrity: sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==} resolution: {integrity: sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw==}
cpu: [arm] engines: {node: ^20.19.0 || >=22.12.0}
os: [linux]
'@rollup/rollup-linux-arm-musleabihf@4.54.0':
resolution: {integrity: sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
'@rollup/rollup-linux-arm64-gnu@4.54.0': '@rolldown/binding-linux-arm64-gnu@1.2.3':
resolution: {integrity: sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==} resolution: {integrity: sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.54.0': '@rolldown/binding-linux-arm64-musl@1.2.3':
resolution: {integrity: sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==} resolution: {integrity: sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.54.0': '@rolldown/binding-linux-ppc64-gnu@1.2.3':
resolution: {integrity: sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==} resolution: {integrity: sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w==}
cpu: [loong64] engines: {node: ^20.19.0 || >=22.12.0}
os: [linux]
'@rollup/rollup-linux-ppc64-gnu@4.54.0':
resolution: {integrity: sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-gnu@4.54.0': '@rolldown/binding-linux-s390x-gnu@1.2.3':
resolution: {integrity: sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==} resolution: {integrity: sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg==}
cpu: [riscv64] engines: {node: ^20.19.0 || >=22.12.0}
os: [linux]
'@rollup/rollup-linux-riscv64-musl@4.54.0':
resolution: {integrity: sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-s390x-gnu@4.54.0':
resolution: {integrity: sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.54.0': '@rolldown/binding-linux-x64-gnu@1.2.3':
resolution: {integrity: sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==} resolution: {integrity: sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.54.0': '@rolldown/binding-linux-x64-musl@1.2.3':
resolution: {integrity: sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==} resolution: {integrity: sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@rollup/rollup-openharmony-arm64@4.54.0': '@rolldown/binding-openharmony-arm64@1.2.3':
resolution: {integrity: sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==} resolution: {integrity: sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [openharmony] os: [openharmony]
'@rollup/rollup-win32-arm64-msvc@4.54.0': '@rolldown/binding-win32-arm64-msvc@1.2.3':
resolution: {integrity: sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==} resolution: {integrity: sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
'@rollup/rollup-win32-ia32-msvc@4.54.0': '@rolldown/binding-win32-x64-msvc@1.2.3':
resolution: {integrity: sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==} resolution: {integrity: sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg==}
cpu: [ia32] engines: {node: ^20.19.0 || >=22.12.0}
os: [win32]
'@rollup/rollup-win32-x64-gnu@4.54.0':
resolution: {integrity: sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==}
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
'@rollup/rollup-win32-x64-msvc@4.54.0': '@rolldown/pluginutils@1.0.1':
resolution: {integrity: sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==} resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
cpu: [x64]
os: [win32]
'@types/estree@1.0.8': address@1.2.2:
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==}
engines: {node: '>= 10.0.0'}
agentkeepalive@3.5.3:
resolution: {integrity: sha512-yqXL+k5rr8+ZRpOAntkaaRgWgE5o8ESAj5DyRmVTCSoZxXmqemb9Dd7T4i5UzwuERdLAJUy6XzR9zFVuf0kzkw==}
engines: {node: '>= 4.0.0'}
ali-oss@6.23.0:
resolution: {integrity: sha512-FipRmyd16Pr/tEey/YaaQ/24Pc3HEpLM9S1DRakEuXlSLXNIJnu1oJtHM53eVYpvW3dXapSjrip3xylZUTIZVQ==}
engines: {node: '>=8'}
ansi-regex@5.0.1: ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
...@@ -314,6 +315,27 @@ packages: ...@@ -314,6 +315,27 @@ packages:
resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
engines: {node: '>=12'} engines: {node: '>=12'}
any-promise@1.3.0:
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
bowser@1.9.4:
resolution: {integrity: sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ==}
builtin-status-codes@3.0.0:
resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==}
call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
call-bound@1.0.4:
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
engines: {node: '>= 0.4'}
chalk@6.0.0:
resolution: {integrity: sha512-2uNTXIuTTxk7ciZgAU1BQcgnchcG0xXnrs6jzkQfj9SsRa9M2s5zE8WT96hS6KmG4MzWHSrvH43DF1m4XRkrFg==}
engines: {node: '>=22'}
color-convert@2.0.1: color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'} engines: {node: '>=7.0.0'}
...@@ -321,24 +343,95 @@ packages: ...@@ -321,24 +343,95 @@ packages:
color-name@1.1.4: color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
content-type@1.0.5:
resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
engines: {node: '>= 0.6'}
copy-to@2.0.1:
resolution: {integrity: sha512-3DdaFaU/Zf1AnpLiFDeNCD4TOWe3Zl2RZaTzUvWiIk5ERzcCodOE20Vqq4fzCbNoHURFHT4/us/Lfq+S2zyY4w==}
core-util-is@1.0.3:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
cross-spawn@7.0.6: cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
dateformat@2.2.0:
resolution: {integrity: sha512-GODcnWq3YGoTnygPfi02ygEiRxqUxpJwuRHjdhJYuxpcZmDq4rjBiXYmbCCzStxo176ixfLT6i4NPwQooRySnw==}
debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
default-user-agent@1.0.0:
resolution: {integrity: sha512-bDF7bg6OSNcSwFWPu4zYKpVkJZQYVrAANMYB8bc9Szem1D0yKdm4sa/rOCs2aC9+2GMqQ7KnwtZRvDhmLF0dXw==}
engines: {node: '>= 0.10.0'}
destroy@1.2.0:
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
detect-libc@2.1.2:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
digest-header@1.1.0:
resolution: {integrity: sha512-glXVh42vz40yZb9Cq2oMOt70FIoWiv+vxNvdKdU8CwjLad25qHM3trLxhl9bVjdr6WaslIXhWpn0NO8T/67Qjg==}
engines: {node: '>= 8.0.0'}
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
eastasianwidth@0.2.0: eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
emoji-regex@8.0.0: emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
emoji-regex@9.2.2: emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
end-of-stream@1.4.5:
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
end-or-error@1.0.1:
resolution: {integrity: sha512-OclLMSug+k2A0JKuf494im25ANRBVW8qsjmwbgX7lQ8P82H21PQ1PWkoYwb9y5yMBS69BPlwtzdIFClo3+7kOQ==}
engines: {node: '>= 0.11.14'}
es-define-property@1.0.1:
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
engines: {node: '>= 0.4'}
es-errors@1.3.0:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
es-object-atoms@1.1.2:
resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==}
engines: {node: '>= 0.4'}
esbuild@0.27.2: esbuild@0.27.2:
resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==}
engines: {node: '>=18'} engines: {node: '>=18'}
hasBin: true hasBin: true
escape-html@1.0.3:
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
extend-shallow@2.0.1:
resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==}
engines: {node: '>=0.10.0'}
fdir@6.5.0: fdir@6.5.0:
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}
...@@ -352,44 +445,232 @@ packages: ...@@ -352,44 +445,232 @@ packages:
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
engines: {node: '>=14'} engines: {node: '>=14'}
formstream@1.5.2:
resolution: {integrity: sha512-NASf0lgxC1AyKNXQIrXTEYkiX99LhCEXTkiGObXAkpBui86a4u8FjH1o2bGb3PpqI3kafC+yw4zWeK6l6VHTgg==}
fsevents@2.3.3: fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin] os: [darwin]
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
get-intrinsic@1.3.0:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'}
get-proto@1.0.1:
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
engines: {node: '>= 0.4'}
get-ready@1.0.0:
resolution: {integrity: sha512-mFXCZPJIlcYcth+N8267+mghfYN9h3EhsDa6JSnbA3Wrhh/XFpuowviFcsDeYZtKspQyWyJqfs4O6P8CHeTwzw==}
glob@11.1.0: glob@11.1.0:
resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==}
engines: {node: 20 || >=22} engines: {node: 20 || >=22}
hasBin: true hasBin: true
gopd@1.2.0:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'}
has-symbols@1.1.0:
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'}
hasown@2.0.4:
resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==}
engines: {node: '>= 0.4'}
humanize-ms@1.2.1:
resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
iconv-lite@0.6.3:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
is-class-hotfix@0.0.6:
resolution: {integrity: sha512-0n+pzCC6ICtVr/WXnN2f03TK/3BfXY7me4cjCAqT8TYXEl0+JBRoqBo94JJHXcyDSLUeWbNX8Fvy5g5RJdAstQ==}
is-extendable@0.1.1:
resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==}
engines: {node: '>=0.10.0'}
is-fullwidth-code-point@3.0.0: is-fullwidth-code-point@3.0.0:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'} engines: {node: '>=8'}
is-type-of@1.4.0:
resolution: {integrity: sha512-EddYllaovi5ysMLMEN7yzHEKh8A850cZ7pykrY1aNRQGn/CDjRDE9qEWbIdt7xGEVJmjBXzU/fNnC4ABTm8tEQ==}
isarray@1.0.0:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
isexe@2.0.0: isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
isstream@0.1.2:
resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==}
jackspeak@4.1.1: jackspeak@4.1.1:
resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==}
engines: {node: 20 || >=22} engines: {node: 20 || >=22}
js-base64@2.6.4:
resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==}
jstoxml@2.2.9:
resolution: {integrity: sha512-OYWlK0j+roh+eyaMROlNbS5cd5R25Y+IUpdl7cNdB8HNrkgwQzIS7L9MegxOiWNBj9dQhA/yAxiMwCC5mwNoBw==}
lightningcss-android-arm64@1.33.0:
resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [android]
lightningcss-darwin-arm64@1.33.0:
resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [darwin]
lightningcss-darwin-x64@1.33.0:
resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [darwin]
lightningcss-freebsd-x64@1.33.0:
resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [freebsd]
lightningcss-linux-arm-gnueabihf@1.33.0:
resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==}
engines: {node: '>= 12.0.0'}
cpu: [arm]
os: [linux]
lightningcss-linux-arm64-gnu@1.33.0:
resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
lightningcss-linux-arm64-musl@1.33.0:
resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
lightningcss-linux-x64-gnu@1.33.0:
resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
lightningcss-linux-x64-musl@1.33.0:
resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [musl]
lightningcss-win32-arm64-msvc@1.33.0:
resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [win32]
lightningcss-win32-x64-msvc@1.33.0:
resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [win32]
lightningcss@1.33.0:
resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==}
engines: {node: '>= 12.0.0'}
lodash@4.18.1:
resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==}
lru-cache@11.2.4: lru-cache@11.2.4:
resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==}
engines: {node: 20 || >=22} engines: {node: 20 || >=22}
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
merge-descriptors@1.0.3:
resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==}
mime@2.6.0:
resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==}
engines: {node: '>=4.0.0'}
hasBin: true
minimatch@10.1.1: minimatch@10.1.1:
resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==}
engines: {node: 20 || >=22} engines: {node: 20 || >=22}
minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
minipass@7.1.2: minipass@7.1.2:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'} engines: {node: '>=16 || 14 >=14.17'}
nanoid@3.3.11: mkdirp@0.5.6:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
hasBin: true
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
mz@2.7.0:
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
nanoid@3.3.17:
resolution: {integrity: sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true hasBin: true
node-hex@1.0.1:
resolution: {integrity: sha512-iwpZdvW6Umz12ICmu9IYPRxg0tOLGmU3Tq2tKetejCj3oZd7b2nUXwP3a7QA5M9glWy8wlPS1G3RwM/CdsUbdQ==}
engines: {node: '>=8.0.0'}
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
object-inspect@1.13.4:
resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
engines: {node: '>= 0.4'}
once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
os-name@1.0.3:
resolution: {integrity: sha512-f5estLO2KN8vgtTRaILIgEGBoBrMnZ3JQ7W9TMZCnOIGwHe8TRGSpcagnWDo+Dfhd/z08k9Xe75hvciJJ8Qaew==}
engines: {node: '>=0.10.0'}
hasBin: true
osx-release@1.1.0:
resolution: {integrity: sha512-ixCMMwnVxyHFQLQnINhmIpWqXIfS2YOXchwQrk+OFzmo6nDjQ0E4KXAyyUh0T0MZgV4bUhkRrAbVqlE4yLVq4A==}
engines: {node: '>=0.10.0'}
hasBin: true
package-json-from-dist@1.0.1: package-json-from-dist@1.0.1:
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
...@@ -401,20 +682,56 @@ packages: ...@@ -401,20 +682,56 @@ packages:
resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==}
engines: {node: 20 || >=22} engines: {node: 20 || >=22}
pause-stream@0.0.11:
resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==}
picocolors@1.1.1: picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
picomatch@4.0.3: picomatch@4.0.5:
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==}
engines: {node: '>=12'} engines: {node: '>=12'}
postcss@8.5.6: platform@1.3.6:
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==}
postcss@8.5.26:
resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==}
engines: {node: ^10 || ^12 || >=14} engines: {node: ^10 || ^12 || >=14}
rollup@4.54.0: process-nextick-args@2.0.1:
resolution: {integrity: sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==} resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
pump@3.0.4:
resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==}
qs@6.15.3:
resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==}
engines: {node: '>=0.6'}
readable-stream@2.3.8:
resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
rolldown@1.2.3:
resolution: {integrity: sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
safe-buffer@5.1.2:
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
sax@1.6.1:
resolution: {integrity: sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==}
engines: {node: '>=11.0.0'}
sdk-base@2.0.1:
resolution: {integrity: sha512-eeG26wRwhtwYuKGCDM3LixCaxY27Pa/5lK4rLKhQa7HBjJ3U3Y+f81MMZQRsDw/8SC2Dao/83yJTXJ8aULuN8Q==}
semver@5.7.2:
resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
hasBin: true hasBin: true
shebang-command@2.0.0: shebang-command@2.0.0:
...@@ -425,6 +742,22 @@ packages: ...@@ -425,6 +742,22 @@ packages:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'} engines: {node: '>=8'}
side-channel-list@1.0.1:
resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==}
engines: {node: '>= 0.4'}
side-channel-map@1.0.1:
resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
engines: {node: '>= 0.4'}
side-channel-weakmap@1.0.2:
resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
engines: {node: '>= 0.4'}
side-channel@1.1.1:
resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==}
engines: {node: '>= 0.4'}
signal-exit@4.1.0: signal-exit@4.1.0:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'} engines: {node: '>=14'}
...@@ -433,6 +766,17 @@ packages: ...@@ -433,6 +766,17 @@ packages:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
statuses@1.5.0:
resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==}
engines: {node: '>= 0.6'}
stream-http@2.8.2:
resolution: {integrity: sha512-QllfrBhqF1DPcz46WxKTs6Mz1Bpc+8Qm6vbqOpVav5odAXwbyzwnEczoWqtxrsmlO+cJqtPrp/8gWKWjaKLLlA==}
stream-wormhole@1.1.0:
resolution: {integrity: sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew==}
engines: {node: '>=4.0.0'}
string-width@4.2.3: string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'} engines: {node: '>=8'}
...@@ -441,6 +785,9 @@ packages: ...@@ -441,6 +785,9 @@ packages:
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
engines: {node: '>=12'} engines: {node: '>=12'}
string_decoder@1.1.1:
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
strip-ansi@6.0.1: strip-ansi@6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'} engines: {node: '>=8'}
...@@ -449,19 +796,53 @@ packages: ...@@ -449,19 +796,53 @@ packages:
resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==}
engines: {node: '>=12'} engines: {node: '>=12'}
tinyglobby@0.2.15: thenify-all@1.6.0:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
engines: {node: '>=0.8'}
thenify@3.3.1:
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
tinyglobby@0.2.17:
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}
vite@7.3.0: to-arraybuffer@1.0.1:
resolution: {integrity: sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==} resolution: {integrity: sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==}
unescape@1.0.1:
resolution: {integrity: sha512-O0+af1Gs50lyH1nUu3ZyYS1cRh01Q/kUKatTOkSs7jukXE6/NebucDVxyiDsA9AQ4JC1V1jUH9EO8JX2nMDgGQ==}
engines: {node: '>=0.10.0'}
urllib@2.44.1:
resolution: {integrity: sha512-vreOVvFizoiIz5NK9IYMgUknkriHHBVccn2VFfJhgKz6O2qwm0SgjFk4OpXFRDXpdrTx8EzM1DB0/pejrqXwPA==}
engines: {node: '>= 0.10.0'}
peerDependencies:
proxy-agent: ^5.0.0
peerDependenciesMeta:
proxy-agent:
optional: true
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
utility@1.18.0:
resolution: {integrity: sha512-PYxZDA+6QtvRvm//++aGdmKG/cI07jNwbROz0Ql+VzFV1+Z0Dy55NI4zZ7RHc9KKpBePNFwoErqIuqQv/cjiTA==}
engines: {node: '>= 0.12.0'}
vite@8.2.1:
resolution: {integrity: sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
'@types/node': ^20.19.0 || >=22.12.0 '@types/node': ^20.19.0 || >=22.12.0
'@vitejs/devtools': ^0.4.0
esbuild: ^0.27.0 || ^0.28.0
jiti: '>=1.21.0' jiti: '>=1.21.0'
less: ^4.0.0 less: ^4.0.0
lightningcss: ^1.21.0
sass: ^1.70.0 sass: ^1.70.0
sass-embedded: ^1.70.0 sass-embedded: ^1.70.0
stylus: '>=0.54.8' stylus: '>=0.54.8'
...@@ -472,12 +853,14 @@ packages: ...@@ -472,12 +853,14 @@ packages:
peerDependenciesMeta: peerDependenciesMeta:
'@types/node': '@types/node':
optional: true optional: true
'@vitejs/devtools':
optional: true
esbuild:
optional: true
jiti: jiti:
optional: true optional: true
less: less:
optional: true optional: true
lightningcss:
optional: true
sass: sass:
optional: true optional: true
sass-embedded: sass-embedded:
...@@ -498,6 +881,10 @@ packages: ...@@ -498,6 +881,10 @@ packages:
engines: {node: '>= 8'} engines: {node: '>= 8'}
hasBin: true hasBin: true
win-release@1.1.1:
resolution: {integrity: sha512-iCRnKVvGxOQdsKhcQId2PXV1vV3J/sDPXKA4Oe9+Eti2nb2ESEsYHRYls/UjoUW3bIc5ZDO8dTH50A/5iVN+bw==}
engines: {node: '>=0.10.0'}
wrap-ansi@7.0.0: wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'} engines: {node: '>=10'}
...@@ -506,6 +893,21 @@ packages: ...@@ -506,6 +893,21 @@ packages:
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
xml2js@0.6.2:
resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==}
engines: {node: '>=4.0.0'}
xmlbuilder@11.0.1:
resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==}
engines: {node: '>=4.0'}
xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}
snapshots: snapshots:
'@esbuild/aix-ppc64@0.27.2': '@esbuild/aix-ppc64@0.27.2':
...@@ -601,73 +1003,88 @@ snapshots: ...@@ -601,73 +1003,88 @@ snapshots:
wrap-ansi: 8.1.0 wrap-ansi: 8.1.0
wrap-ansi-cjs: wrap-ansi@7.0.0 wrap-ansi-cjs: wrap-ansi@7.0.0
'@rollup/rollup-android-arm-eabi@4.54.0': '@oxc-project/types@0.143.0': {}
optional: true
'@rollup/rollup-android-arm64@4.54.0':
optional: true
'@rollup/rollup-darwin-arm64@4.54.0': '@rolldown/binding-android-arm64@1.2.3':
optional: true optional: true
'@rollup/rollup-darwin-x64@4.54.0': '@rolldown/binding-darwin-arm64@1.2.3':
optional: true optional: true
'@rollup/rollup-freebsd-arm64@4.54.0': '@rolldown/binding-darwin-x64@1.2.3':
optional: true optional: true
'@rollup/rollup-freebsd-x64@4.54.0': '@rolldown/binding-freebsd-x64@1.2.3':
optional: true optional: true
'@rollup/rollup-linux-arm-gnueabihf@4.54.0': '@rolldown/binding-linux-arm-gnueabihf@1.2.3':
optional: true optional: true
'@rollup/rollup-linux-arm-musleabihf@4.54.0': '@rolldown/binding-linux-arm64-gnu@1.2.3':
optional: true optional: true
'@rollup/rollup-linux-arm64-gnu@4.54.0': '@rolldown/binding-linux-arm64-musl@1.2.3':
optional: true optional: true
'@rollup/rollup-linux-arm64-musl@4.54.0': '@rolldown/binding-linux-ppc64-gnu@1.2.3':
optional: true optional: true
'@rollup/rollup-linux-loong64-gnu@4.54.0': '@rolldown/binding-linux-s390x-gnu@1.2.3':
optional: true optional: true
'@rollup/rollup-linux-ppc64-gnu@4.54.0': '@rolldown/binding-linux-x64-gnu@1.2.3':
optional: true optional: true
'@rollup/rollup-linux-riscv64-gnu@4.54.0': '@rolldown/binding-linux-x64-musl@1.2.3':
optional: true optional: true
'@rollup/rollup-linux-riscv64-musl@4.54.0': '@rolldown/binding-openharmony-arm64@1.2.3':
optional: true optional: true
'@rollup/rollup-linux-s390x-gnu@4.54.0': '@rolldown/binding-win32-arm64-msvc@1.2.3':
optional: true optional: true
'@rollup/rollup-linux-x64-gnu@4.54.0': '@rolldown/binding-win32-x64-msvc@1.2.3':
optional: true optional: true
'@rollup/rollup-linux-x64-musl@4.54.0': '@rolldown/pluginutils@1.0.1': {}
optional: true
'@rollup/rollup-openharmony-arm64@4.54.0': address@1.2.2: {}
optional: true
'@rollup/rollup-win32-arm64-msvc@4.54.0': agentkeepalive@3.5.3:
optional: true dependencies:
humanize-ms: 1.2.1
'@rollup/rollup-win32-ia32-msvc@4.54.0':
optional: true
'@rollup/rollup-win32-x64-gnu@4.54.0':
optional: true
'@rollup/rollup-win32-x64-msvc@4.54.0':
optional: true
'@types/estree@1.0.8': {} ali-oss@6.23.0:
dependencies:
address: 1.2.2
agentkeepalive: 3.5.3
bowser: 1.9.4
copy-to: 2.0.1
dateformat: 2.2.0
debug: 4.4.3
destroy: 1.2.0
end-or-error: 1.0.1
get-ready: 1.0.0
humanize-ms: 1.2.1
is-type-of: 1.4.0
js-base64: 2.6.4
jstoxml: 2.2.9
lodash: 4.18.1
merge-descriptors: 1.0.3
mime: 2.6.0
platform: 1.3.6
pump: 3.0.4
qs: 6.15.3
sdk-base: 2.0.1
stream-http: 2.8.2
stream-wormhole: 1.1.0
urllib: 2.44.1
utility: 1.18.0
xml2js: 0.6.2
transitivePeerDependencies:
- proxy-agent
- supports-color
ansi-regex@5.0.1: {} ansi-regex@5.0.1: {}
...@@ -679,24 +1096,86 @@ snapshots: ...@@ -679,24 +1096,86 @@ snapshots:
ansi-styles@6.2.3: {} ansi-styles@6.2.3: {}
any-promise@1.3.0: {}
bowser@1.9.4: {}
builtin-status-codes@3.0.0: {}
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
function-bind: 1.1.2
call-bound@1.0.4:
dependencies:
call-bind-apply-helpers: 1.0.2
get-intrinsic: 1.3.0
chalk@6.0.0: {}
color-convert@2.0.1: color-convert@2.0.1:
dependencies: dependencies:
color-name: 1.1.4 color-name: 1.1.4
color-name@1.1.4: {} color-name@1.1.4: {}
content-type@1.0.5: {}
copy-to@2.0.1: {}
core-util-is@1.0.3: {}
cross-spawn@7.0.6: cross-spawn@7.0.6:
dependencies: dependencies:
path-key: 3.1.1 path-key: 3.1.1
shebang-command: 2.0.0 shebang-command: 2.0.0
which: 2.0.2 which: 2.0.2
dateformat@2.2.0: {}
debug@4.4.3:
dependencies:
ms: 2.1.3
default-user-agent@1.0.0:
dependencies:
os-name: 1.0.3
destroy@1.2.0: {}
detect-libc@2.1.2: {}
digest-header@1.1.0: {}
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
es-errors: 1.3.0
gopd: 1.2.0
eastasianwidth@0.2.0: {} eastasianwidth@0.2.0: {}
ee-first@1.1.1: {}
emoji-regex@8.0.0: {} emoji-regex@8.0.0: {}
emoji-regex@9.2.2: {} emoji-regex@9.2.2: {}
end-of-stream@1.4.5:
dependencies:
once: 1.4.0
end-or-error@1.0.1: {}
es-define-property@1.0.1: {}
es-errors@1.3.0: {}
es-object-atoms@1.1.2:
dependencies:
es-errors: 1.3.0
esbuild@0.27.2: esbuild@0.27.2:
optionalDependencies: optionalDependencies:
'@esbuild/aix-ppc64': 0.27.2 '@esbuild/aix-ppc64': 0.27.2
...@@ -725,19 +1204,55 @@ snapshots: ...@@ -725,19 +1204,55 @@ snapshots:
'@esbuild/win32-arm64': 0.27.2 '@esbuild/win32-arm64': 0.27.2
'@esbuild/win32-ia32': 0.27.2 '@esbuild/win32-ia32': 0.27.2
'@esbuild/win32-x64': 0.27.2 '@esbuild/win32-x64': 0.27.2
optional: true
escape-html@1.0.3: {}
fdir@6.5.0(picomatch@4.0.3): extend-shallow@2.0.1:
dependencies:
is-extendable: 0.1.1
fdir@6.5.0(picomatch@4.0.5):
optionalDependencies: optionalDependencies:
picomatch: 4.0.3 picomatch: 4.0.5
foreground-child@3.3.1: foreground-child@3.3.1:
dependencies: dependencies:
cross-spawn: 7.0.6 cross-spawn: 7.0.6
signal-exit: 4.1.0 signal-exit: 4.1.0
formstream@1.5.2:
dependencies:
destroy: 1.2.0
mime: 2.6.0
node-hex: 1.0.1
pause-stream: 0.0.11
fsevents@2.3.3: fsevents@2.3.3:
optional: true optional: true
function-bind@1.1.2: {}
get-intrinsic@1.3.0:
dependencies:
call-bind-apply-helpers: 1.0.2
es-define-property: 1.0.1
es-errors: 1.3.0
es-object-atoms: 1.1.2
function-bind: 1.1.2
get-proto: 1.0.1
gopd: 1.2.0
has-symbols: 1.1.0
hasown: 2.0.4
math-intrinsics: 1.1.0
get-proto@1.0.1:
dependencies:
dunder-proto: 1.0.1
es-object-atoms: 1.1.2
get-ready@1.0.0: {}
glob@11.1.0: glob@11.1.0:
dependencies: dependencies:
foreground-child: 3.3.1 foreground-child: 3.3.1
...@@ -747,23 +1262,149 @@ snapshots: ...@@ -747,23 +1262,149 @@ snapshots:
package-json-from-dist: 1.0.1 package-json-from-dist: 1.0.1
path-scurry: 2.0.1 path-scurry: 2.0.1
gopd@1.2.0: {}
has-symbols@1.1.0: {}
hasown@2.0.4:
dependencies:
function-bind: 1.1.2
humanize-ms@1.2.1:
dependencies:
ms: 2.1.3
iconv-lite@0.6.3:
dependencies:
safer-buffer: 2.1.2
inherits@2.0.4: {}
is-class-hotfix@0.0.6: {}
is-extendable@0.1.1: {}
is-fullwidth-code-point@3.0.0: {} is-fullwidth-code-point@3.0.0: {}
is-type-of@1.4.0:
dependencies:
core-util-is: 1.0.3
is-class-hotfix: 0.0.6
isstream: 0.1.2
isarray@1.0.0: {}
isexe@2.0.0: {} isexe@2.0.0: {}
isstream@0.1.2: {}
jackspeak@4.1.1: jackspeak@4.1.1:
dependencies: dependencies:
'@isaacs/cliui': 8.0.2 '@isaacs/cliui': 8.0.2
js-base64@2.6.4: {}
jstoxml@2.2.9: {}
lightningcss-android-arm64@1.33.0:
optional: true
lightningcss-darwin-arm64@1.33.0:
optional: true
lightningcss-darwin-x64@1.33.0:
optional: true
lightningcss-freebsd-x64@1.33.0:
optional: true
lightningcss-linux-arm-gnueabihf@1.33.0:
optional: true
lightningcss-linux-arm64-gnu@1.33.0:
optional: true
lightningcss-linux-arm64-musl@1.33.0:
optional: true
lightningcss-linux-x64-gnu@1.33.0:
optional: true
lightningcss-linux-x64-musl@1.33.0:
optional: true
lightningcss-win32-arm64-msvc@1.33.0:
optional: true
lightningcss-win32-x64-msvc@1.33.0:
optional: true
lightningcss@1.33.0:
dependencies:
detect-libc: 2.1.2
optionalDependencies:
lightningcss-android-arm64: 1.33.0
lightningcss-darwin-arm64: 1.33.0
lightningcss-darwin-x64: 1.33.0
lightningcss-freebsd-x64: 1.33.0
lightningcss-linux-arm-gnueabihf: 1.33.0
lightningcss-linux-arm64-gnu: 1.33.0
lightningcss-linux-arm64-musl: 1.33.0
lightningcss-linux-x64-gnu: 1.33.0
lightningcss-linux-x64-musl: 1.33.0
lightningcss-win32-arm64-msvc: 1.33.0
lightningcss-win32-x64-msvc: 1.33.0
lodash@4.18.1: {}
lru-cache@11.2.4: {} lru-cache@11.2.4: {}
math-intrinsics@1.1.0: {}
merge-descriptors@1.0.3: {}
mime@2.6.0: {}
minimatch@10.1.1: minimatch@10.1.1:
dependencies: dependencies:
'@isaacs/brace-expansion': 5.0.0 '@isaacs/brace-expansion': 5.0.0
minimist@1.2.8: {}
minipass@7.1.2: {} minipass@7.1.2: {}
nanoid@3.3.11: {} mkdirp@0.5.6:
dependencies:
minimist: 1.2.8
ms@2.1.3: {}
mz@2.7.0:
dependencies:
any-promise: 1.3.0
object-assign: 4.1.1
thenify-all: 1.6.0
nanoid@3.3.17: {}
node-hex@1.0.1: {}
object-assign@4.1.1: {}
object-inspect@1.13.4: {}
once@1.4.0:
dependencies:
wrappy: 1.0.2
os-name@1.0.3:
dependencies:
osx-release: 1.1.0
win-release: 1.1.1
osx-release@1.1.0:
dependencies:
minimist: 1.2.8
package-json-from-dist@1.0.1: {} package-json-from-dist@1.0.1: {}
...@@ -774,43 +1415,75 @@ snapshots: ...@@ -774,43 +1415,75 @@ snapshots:
lru-cache: 11.2.4 lru-cache: 11.2.4
minipass: 7.1.2 minipass: 7.1.2
pause-stream@0.0.11:
dependencies:
through: 2.3.8
picocolors@1.1.1: {} picocolors@1.1.1: {}
picomatch@4.0.3: {} picomatch@4.0.5: {}
platform@1.3.6: {}
postcss@8.5.6: postcss@8.5.26:
dependencies: dependencies:
nanoid: 3.3.11 nanoid: 3.3.17
picocolors: 1.1.1 picocolors: 1.1.1
source-map-js: 1.2.1 source-map-js: 1.2.1
rollup@4.54.0: process-nextick-args@2.0.1: {}
pump@3.0.4:
dependencies:
end-of-stream: 1.4.5
once: 1.4.0
qs@6.15.3:
dependencies:
es-define-property: 1.0.1
side-channel: 1.1.1
readable-stream@2.3.8:
dependencies:
core-util-is: 1.0.3
inherits: 2.0.4
isarray: 1.0.0
process-nextick-args: 2.0.1
safe-buffer: 5.1.2
string_decoder: 1.1.1
util-deprecate: 1.0.2
rolldown@1.2.3:
dependencies: dependencies:
'@types/estree': 1.0.8 '@oxc-project/types': 0.143.0
'@rolldown/pluginutils': 1.0.1
optionalDependencies: optionalDependencies:
'@rollup/rollup-android-arm-eabi': 4.54.0 '@rolldown/binding-android-arm64': 1.2.3
'@rollup/rollup-android-arm64': 4.54.0 '@rolldown/binding-darwin-arm64': 1.2.3
'@rollup/rollup-darwin-arm64': 4.54.0 '@rolldown/binding-darwin-x64': 1.2.3
'@rollup/rollup-darwin-x64': 4.54.0 '@rolldown/binding-freebsd-x64': 1.2.3
'@rollup/rollup-freebsd-arm64': 4.54.0 '@rolldown/binding-linux-arm-gnueabihf': 1.2.3
'@rollup/rollup-freebsd-x64': 4.54.0 '@rolldown/binding-linux-arm64-gnu': 1.2.3
'@rollup/rollup-linux-arm-gnueabihf': 4.54.0 '@rolldown/binding-linux-arm64-musl': 1.2.3
'@rollup/rollup-linux-arm-musleabihf': 4.54.0 '@rolldown/binding-linux-ppc64-gnu': 1.2.3
'@rollup/rollup-linux-arm64-gnu': 4.54.0 '@rolldown/binding-linux-s390x-gnu': 1.2.3
'@rollup/rollup-linux-arm64-musl': 4.54.0 '@rolldown/binding-linux-x64-gnu': 1.2.3
'@rollup/rollup-linux-loong64-gnu': 4.54.0 '@rolldown/binding-linux-x64-musl': 1.2.3
'@rollup/rollup-linux-ppc64-gnu': 4.54.0 '@rolldown/binding-openharmony-arm64': 1.2.3
'@rollup/rollup-linux-riscv64-gnu': 4.54.0 '@rolldown/binding-win32-arm64-msvc': 1.2.3
'@rollup/rollup-linux-riscv64-musl': 4.54.0 '@rolldown/binding-win32-x64-msvc': 1.2.3
'@rollup/rollup-linux-s390x-gnu': 4.54.0
'@rollup/rollup-linux-x64-gnu': 4.54.0 safe-buffer@5.1.2: {}
'@rollup/rollup-linux-x64-musl': 4.54.0
'@rollup/rollup-openharmony-arm64': 4.54.0 safer-buffer@2.1.2: {}
'@rollup/rollup-win32-arm64-msvc': 4.54.0
'@rollup/rollup-win32-ia32-msvc': 4.54.0 sax@1.6.1: {}
'@rollup/rollup-win32-x64-gnu': 4.54.0
'@rollup/rollup-win32-x64-msvc': 4.54.0 sdk-base@2.0.1:
fsevents: 2.3.3 dependencies:
get-ready: 1.0.0
semver@5.7.2: {}
shebang-command@2.0.0: shebang-command@2.0.0:
dependencies: dependencies:
...@@ -818,10 +1491,50 @@ snapshots: ...@@ -818,10 +1491,50 @@ snapshots:
shebang-regex@3.0.0: {} shebang-regex@3.0.0: {}
side-channel-list@1.0.1:
dependencies:
es-errors: 1.3.0
object-inspect: 1.13.4
side-channel-map@1.0.1:
dependencies:
call-bound: 1.0.4
es-errors: 1.3.0
get-intrinsic: 1.3.0
object-inspect: 1.13.4
side-channel-weakmap@1.0.2:
dependencies:
call-bound: 1.0.4
es-errors: 1.3.0
get-intrinsic: 1.3.0
object-inspect: 1.13.4
side-channel-map: 1.0.1
side-channel@1.1.1:
dependencies:
es-errors: 1.3.0
object-inspect: 1.13.4
side-channel-list: 1.0.1
side-channel-map: 1.0.1
side-channel-weakmap: 1.0.2
signal-exit@4.1.0: {} signal-exit@4.1.0: {}
source-map-js@1.2.1: {} source-map-js@1.2.1: {}
statuses@1.5.0: {}
stream-http@2.8.2:
dependencies:
builtin-status-codes: 3.0.0
inherits: 2.0.4
readable-stream: 2.3.8
to-arraybuffer: 1.0.1
xtend: 4.0.2
stream-wormhole@1.1.0: {}
string-width@4.2.3: string-width@4.2.3:
dependencies: dependencies:
emoji-regex: 8.0.0 emoji-regex: 8.0.0
...@@ -834,6 +1547,10 @@ snapshots: ...@@ -834,6 +1547,10 @@ snapshots:
emoji-regex: 9.2.2 emoji-regex: 9.2.2
strip-ansi: 7.1.2 strip-ansi: 7.1.2
string_decoder@1.1.1:
dependencies:
safe-buffer: 5.1.2
strip-ansi@6.0.1: strip-ansi@6.0.1:
dependencies: dependencies:
ansi-regex: 5.0.1 ansi-regex: 5.0.1
...@@ -842,26 +1559,71 @@ snapshots: ...@@ -842,26 +1559,71 @@ snapshots:
dependencies: dependencies:
ansi-regex: 6.2.2 ansi-regex: 6.2.2
tinyglobby@0.2.15: thenify-all@1.6.0:
dependencies: dependencies:
fdir: 6.5.0(picomatch@4.0.3) thenify: 3.3.1
picomatch: 4.0.3
vite@7.3.0: thenify@3.3.1:
dependencies: dependencies:
esbuild: 0.27.2 any-promise: 1.3.0
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3 through@2.3.8: {}
postcss: 8.5.6
rollup: 4.54.0 tinyglobby@0.2.17:
tinyglobby: 0.2.15 dependencies:
fdir: 6.5.0(picomatch@4.0.5)
picomatch: 4.0.5
to-arraybuffer@1.0.1: {}
unescape@1.0.1:
dependencies:
extend-shallow: 2.0.1
urllib@2.44.1:
dependencies:
any-promise: 1.3.0
content-type: 1.0.5
default-user-agent: 1.0.0
digest-header: 1.1.0
ee-first: 1.1.1
formstream: 1.5.2
humanize-ms: 1.2.1
iconv-lite: 0.6.3
pump: 3.0.4
qs: 6.15.3
statuses: 1.5.0
utility: 1.18.0
util-deprecate@1.0.2: {}
utility@1.18.0:
dependencies:
copy-to: 2.0.1
escape-html: 1.0.3
mkdirp: 0.5.6
mz: 2.7.0
unescape: 1.0.1
vite@8.2.1(esbuild@0.27.2):
dependencies:
lightningcss: 1.33.0
picomatch: 4.0.5
postcss: 8.5.26
rolldown: 1.2.3
tinyglobby: 0.2.17
optionalDependencies: optionalDependencies:
esbuild: 0.27.2
fsevents: 2.3.3 fsevents: 2.3.3
which@2.0.2: which@2.0.2:
dependencies: dependencies:
isexe: 2.0.0 isexe: 2.0.0
win-release@1.1.1:
dependencies:
semver: 5.7.2
wrap-ansi@7.0.0: wrap-ansi@7.0.0:
dependencies: dependencies:
ansi-styles: 4.3.0 ansi-styles: 4.3.0
...@@ -873,3 +1635,14 @@ snapshots: ...@@ -873,3 +1635,14 @@ snapshots:
ansi-styles: 6.2.3 ansi-styles: 6.2.3
string-width: 5.1.2 string-width: 5.1.2
strip-ansi: 7.1.2 strip-ansi: 7.1.2
wrappy@1.0.2: {}
xml2js@0.6.2:
dependencies:
sax: 1.6.1
xmlbuilder: 11.0.1
xmlbuilder@11.0.1: {}
xtend@4.0.2: {}
window.BOOK_API_BASE_URL = 'https://project-api.ezijing.com/api/book';
...@@ -69,27 +69,25 @@ request.interceptors.request.use( ...@@ -69,27 +69,25 @@ request.interceptors.request.use(
return config; return config;
}, },
function (error) { function (error) {
return error;
return Promise.reject(error) return Promise.reject(error)
} }
); );
// 响应拦截器 // 响应拦截器
request.interceptors.response.use( request.interceptors.response.use(
function (response) { function (response) {
if (response.status === 200) { if (response.status === 200) {
if (response.data && response.data.code === 200) { if (response.data && response.data.code === 200) {
return response.data; return response.data;
} else { } else {
return response; return response;
} }
} else { } else {
return response; return response;
} }
return false;
}, },
function (error) { function (error) {
const str = `message: ${error.message} \n data: ${error.config.data} \n token: ${error.config.headers.Authorization}`;
return error;
return Promise.reject(error); return Promise.reject(error);
} }
); );
window.readerRequest = request;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=0"
/>
<title>阅读详情-紫荆教育云教材</title>
<link rel="stylesheet" href="assets/css/common.css" />
<link rel="stylesheet" href="assets/css/read.css" />
<link rel="stylesheet" href="assets/css/github.css" />
<script src="assets/js/flexible.min.js"></script>
<script src="assets/js/highlight.min.js"></script>
<script src="assets/js/vue@2.js"></script>
<script src="assets/js/jquery.2.2.4.min.js"></script>
<script src="assets/js/qs.6.11.2.min.js"></script>
<script src="assets/js/blueimp-md5.2.19.0.min.js"></script>
<script src="assets/js/axios.min.js"></script>
<script src="assets/js/request.js"></script>
<script src="assets/js/iconfont.js"></script>
<script>
function countSubstringOccurrences(str, substring) {
// 如果子串没有出现,match方法将返回null
if (!substring) return 0;
// 使用正则表达式创建全局搜索模式
const regex = new RegExp(substring, "g");
// 使用match方法查找所有匹配项
const matches = str.match(regex);
// 如果没有匹配项,返回0
if (!matches) return 0;
// 返回匹配项的数量,即子串出现的次数
return matches.length;
}
</script>
<!-- <script src="assets/js/vconsole.min.js"></script> -->
</head>
<body class="body_evaluating">
<div id="app" v-cloak>
<div class="chapter-container" ref="chapterContainer">
<div
class="load-chapter prev-chapter"
v-show="prev_chapter && scrollLoading"
>
<div class="chapter-c-title">
上一节: <strong>{{ prev_chapter.name }}</strong>
</div>
</div>
<div
class="load-chapter next-chapter"
v-show="next_chapter && scrollLoading"
>
<div class="chapter-c-title">下一节: {{ next_chapter.name }}</div>
</div>
<div class="chapter-content-list" id="chapter-content-list">
<div class="show-up">
<div
class="chapter-item-up"
:class="[`chapter-item-up-${prev_chapter.id}`]"
id="chapter-item-up"
:data-id="prev_chapter.id"
v-html="prev_chapter.contents"
></div>
</div>
<div class="show">
<div
class="chapter-item"
:class="[`chapter-item-${chapter_id}`]"
id="chapter-item"
:data-id="chapter_id"
v-html="chapterContent"
v-show="!lockedContent"
></div>
<div class="lock-content" v-show="lockedContent">
<div class="lock-img">
<div class="img"></div>
</div>
<div class="lock-desc">未解锁章节</div>
<div class="lock-button">
<span>获取完整版</span>
</div>
</div>
</div>
</div>
<div class="range-controll">
<div
class="controll-container"
v-show="boolControllStatus"
:class="[nowColorId > 0 ? 'colorLine' : '']"
>
<!-- <div class="controll-container" :class="[nowColorId > 0 ? 'colorLine' : '']"> -->
<div class="controll-position">
<div
v-show="rangeMenuStatus"
class="com prev"
@click.stop.prevent="rangeMenuMove('prev', $event)"
><i class="ico"></i></div>
<div class="controll-list-container">
<div class="controll-list">
<div class="controll-item colorLine" v-if="nowColorId > 0">
<span
v-for="(item, index) in colorList"
:class="[`c-${item}`]"
:key="index"
@click.stop.prevent="rangeTextChangeLight"
></span>
</div>
<div
class="controll-item"
@click.stop.prevent="rangeTextInFlutter('discuss', $event)"
>
<div class="opa discuss"><i class="ico"></i></div>
<p>讨论</p>
</div>
<div
class="controll-item"
@click.stop.prevent="rangeTextInFlutter('note', $event)"
>
<div class="opa note"><i class="ico"></i></div>
<p>笔记</p>
</div>
<div
class="controll-item"
@click.stop.prevent="rangeTextInLine($event)"
>
<div class="opa line"><i class="ico"></i></div>
<p v-if="nowLineId > 0">移除划线</p>
<p v-else-if="expandLine.length > 0">扩展划线</p>
<p class="cancel" v-else>划线</p>
</div>
<div
class="controll-item light-color"
@click.stop.prevent="rangeTextInLight($event)"
>
<div class="opa light"><i class="ico"></i></div>
<p v-if="nowColorId > 0">移除高亮</p>
<p v-else-if="expandLight.length > 0">扩展高亮</p>
<p class="cancel" v-else>高亮</p>
</div>
<div
class="controll-item"
@click.stop.prevent="rangeTextInFlutter('baike', $event)"
>
<div class="opa baike"><i class="ico"></i></div>
<p>百科</p>
</div>
<div
class="controll-item"
@click.stop.prevent="rangeTextInFlutter('dict', $event)"
>
<div class="opa dict"><i class="ico"></i></div>
<p>字典</p>
</div>
<div
class="controll-item"
@click.stop.prevent="rangeTextInFlutter('read', $event)"
>
<div class="opa read"><i class="ico"></i></div>
<p>朗读</p>
</div>
</div>
</div>
<div
v-show="!rangeMenuStatus"
class="com next"
@click.stop.prevent="rangeMenuMove('next', $event)"
><i class="ico"></i></div>
</div>
<div class="controll-triangle top" v-show="rangeSquareType === 'top'"></div>
<div class="controll-triangle bottom" v-show="rangeSquareType === 'bottom'"></div>
</div>
<!-- light -->
<div class="controll-light-container" v-show="boolLightColor">
<div class="controll-color-list">
<div
class="controll-color-item"
:class="[`c-${item}`, nowActiveColor === item ? 'active' : '']"
v-for="(item, index) in colorList"
:key="index"
@click="setLightColorText(item)"
>
<div class="color"></div>
</div>
</div>
</div>
</div>
<div class="tange-tooptip" v-show="tooltipId">
<div class="square" :style=""></div>
<div class="tooltip-content-container">
<div class="tooltip-content">
<div class="content">
<p>{{tooltipContent.content}}</p>
</div>
<div class="content-opa" v-if="tooltipContent.link">
<a
:href="tooltipContent.link"
target="_blank"
rel="noreferrer"
class="c-link"
>
<svg class="svg-icon" style="color: #ab1941; fill: currentColor; overflow: hidden; width: 20px; height: 20px;" aria-hidden="true"><use xlink:href="#icon-a-link"></use></svg>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
let getChapterContentURL = "/v1/book/Information/getSectionContent"; // 获取书籍的内容
let getChapterContentProgressURL = "/v1/book/Information/addProgress"; // 添加学习进度,每章节进度
let getChapterContentRecordURL = "/v1/book/Information/addReadRecord"; // 添加阅读记录
let addInfomationNotes = "/v1/book/Information/addNotes"; // 添加划线,高亮
let editInfomationNotes = "/v1/book/Information/editNotes"; // 编辑划线,高亮
let delInfomationNotes = "/v1/book/Information/delNotes"; // 删除划线,高亮
let delBatchInfomationNotes = "/v1/book/Information/delListNotes"; // 删除划线,高亮
let checkPracticeInPosition = "/v1/book/Information/checkChapterQuestion"; // 是否答题检测
let checkTooptipInPosition = "/v1/book/Information/getBubbleByPosition"; // 获取气泡内容
// var vConsole = new VConsole();
var app = new Vue({
el: "#app",
data: {
docmHeight: document.documentElement.clientHeight || document.body.clientHeight,
clientWidth: document.documentElement.clientWidth || document.body.clientWidth,
windowHeight: 0, // 视口的高度
upContentsHeight: 0,
colorList: [
"feff00",
"00ff00",
"00ffff",
"ff00ff",
"d2b3fa",
"ff4500",
],
hideshow: true, //显示或者隐藏footer
baseURL: "",
book_id: "", // 358
chapter_id: "", // 1931 343 2821
token: "", // token
searchText: "",
rangeOperaTextHeight: 0, // 划线高亮
rangeMenuStatus: false,
rangeOperaColorHeight: 0, // 颜色选择
chapterContent: "", // 每个章节内容
chapterContentHeight: "", // 每个章节的高度
next_chapter: "", // 下一个章节的id
prev_chapter: "", // 上一个章节的id
rangeSquareType: 'top',
nextChapterLoading: false,
scrollTop: 0,
readTimer: null,
debounceTimeout: false, // 防抖设置
selectedText: "", // 选择的文本
boolControllStatus: false, // 操作组件
boolLightColor: false, // 颜色选择组件
tempCloneRange: null, // 临时选区对象
selectRangeTimeout: null, // range 防抖
line_list: [],
nowLineId: -1,
color_line: [],
nowColorId: -1,
touchIn: false, // 按下
touchstartTime: null, // 按下的时间
touchLastClickTime: null, // 按下时间-双击检测
nowActiveColor: "",
startOffset: 0,
endOffset: 0,
selectRangeType: "",
lineAndLight: false,
expandLine: [], // 待扩展的划线
expandLight: [], // 待扩展的高亮
expandIds: null, // 待删除
selectRangeArr: [],
rangePosition: {},
side: {},
blockTagNames: [
"p",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"div",
"li",
"dd",
"dt",
],
isMobile: "ontouchstart" in window || navigator.maxTouchPoints,
containerHasType: false,
translateY: 0, // 默认的初始位置
scrollPageY: 0, // 鼠标或手指按下的位置
scrollLoading: false, // 鼠标或手指按下的位置
tooltipStatus: null, // 气泡展示状态
tooltipId: null, // 气泡id
tooltipContent: {}, // 气泡内容
noteId: -1, //
velocity: 0.6, // 初始速度
friction: 0.03, // 摩擦系数
animationId: null, // 动画id
animationType: null, // 上滑还是下滑
lockedContent: false, // 章节被锁
},
created() {},
async mounted() {
this.windowHeight = document.documentElement.clientHeight;
this.chapterContent = null;
this.rangeOperaTextHeight = $(".controll-container").outerHeight(
true
);
this.rangeOperaColorHeight = $(
".controll-light-container"
).outerHeight(true);
// selection range 设置
this.selectionRangeOperation();
},
unmounted() {
clearInterval(this.readTimer);
},
watch: {
side(newVal, oldVal) {
if (newVal) {
this.calcPositionRange(newVal);
}
},
},
methods: {
async getBookChapterData(chapterId, scroll) {
if (this.nextChapterLoading) return;
this.chapterContent = "";
$("#chapter-content-list .show").css({
transform: `translateY(0px)`,
WebkitTranform: `translateY(0px)`,
});
this.nextChapterLoading = true;
const chapter_id = chapterId || this.chapter_id;
this.initArags();
this.clearRangeContent();
if (!this.book_id || !chapter_id) return;
// loading
this.callHandlerNoticeFlutterSubmit("showLoadingCallback");
let url = `${this.baseURL}${getChapterContentURL}`;
const res = await request.post(url, {
book_id: this.book_id,
chapter_id: chapter_id,
});
if (res && (res.code === 200 || res.code === "200")) {
this.lockedContent = false;
this.next_chapter = res.data.next_chapter; // 下一节的内容
this.prev_chapter = res.data.up_chapter; // 上一节的内容
this.color_line = res.data.color_line; // 高亮
this.line_list = res.data.line_list; // 划线
if (res.data.content) {
this.chapterContent = res.data.content;
this.initArags();
this.clearRangeContent();
this.callHandlerNoticeFlutterSubmit("dismissLoadingCallback");
document.querySelectorAll('pre code').forEach((el) => {
hljs.highlightElement(el);
});
// 带内容加载完成
await this.waitForContentLoad();
await this.replaceVideoToAudio();
if (scroll) {
$(".chapter-content-list .show").css({
transform: `translateY(-${this.upContentsHeight - this.windowHeight}px)`,
WebkitTranform: `translateY(-${this.upContentsHeight - this.windowHeight}px)`,
})
}
this.searchTextInSide();
// 计算设置上一节的高度
this.upContentsHeight = $(".chapter-item-up").innerHeight();
// 进度设置
await this.uploadRead();
await this.line_listFilterShow();
this.nextChapterLoading = false;
await this.toNoteIdSide();
// 各类事件
this.listenHandlerEvents();
// 上报事件
this.uploadReadInterval();
} else {
this.callHandlerNoticeFlutterSubmit("dismissLoadingCallback");
this.nextChapterLoading = false;
}
} else {
if (res && res.data && res.data.code === 3001) {
clearInterval(this.readTimer);
this.lockedContent = true;
this.callHandlerNoticeFlutterSubmit("dismissLoadingCallback");
} else if (res && res.response.status === 403) {
this.nextChapterLoading = false;
this.callHandlerNoticeFlutterSubmit("refreshTokenCallback");
}
}
},
replaceVideoToAudio() {
const allVideo = document.querySelectorAll("video");
let arr = ['.mp3', '.wma', '.wmv', '.aac', '.flac']
if (allVideo.length > 0) {
allVideo.forEach((item, index) => {
var sources = $(item).find("source");
let src = '';
let suffix = '';
if (sources.length === 1) {
src = $(sources[0]).attr("src");
suffix = src.substring(src.lastIndexOf('.'));
}
if (arr.includes(suffix.toLowerCase())) {
const html = `<audio controls><source src="${src}" type="audio/${suffix.substring(1)}"></audio>`;
$(html).insertBefore($(item));
$(item).remove();
}
})
}
},
// 添加学习进度
async uploadReadProgress(chapter_id, progress) {
if (!this.token) return;
let url = `${this.baseURL}${getChapterContentProgressURL}`;
const chapterId = chapter_id || this.chapter_id;
const res = await request.post(url, {
book_id: this.book_id,
chapter_id: chapterId,
progress: progress,
});
},
// 添加阅读记录
async uploadReadRecord(chapter_id, progress) {
if (!this.token) return;
let url = `${this.baseURL}${getChapterContentRecordURL}`;
const chapterId = chapter_id || this.chapter_id;
const res = await request.post(url, {
book_id: this.book_id,
chapter_id: chapterId,
positioning: progress,
});
},
// 阅读进度上报
async uploadRead() {
let url = `${this.baseURL}${getChapterContentProgressURL}`;
const transformValue = $("#chapter-content-list .show").css(
"transform"
);
var matches = transformValue.match(
/matrix\(\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*\)/
);
const translateY = matches[6] ? parseInt(matches[6]) : 0;
let maxTop = this.chapterContentHeight - this.docmHeight;
// 章节内容不到没有撑满整个屏幕
let progress = 0;
if (this.chapterContentHeight <= this.docmHeight) {
progress = 100;
await this.uploadReadProgress(null, progress);
await this.uploadReadRecord(null, progress);
} else {
if (translateY >= 0) {
progress = parseInt((this.docmHeight / this.chapterContentHeight) * 100);
} else {
let scaple = (this.docmHeight + Math.abs(translateY)) / this.chapterContentHeight;
progress = parseInt(scaple* 100);
}
await this.uploadReadProgress(null, progress);
await this.uploadReadRecord(null, progress);
}
},
// 上报的定时器
uploadReadInterval() {
clearInterval(this.readTimer);
this.readTimer = setInterval(() => {
this.uploadRead();
}, 5000);
},
// 全文搜索设置
searchTextInSide() {
if (this.searchText) {
const allChildren = $(".chapter-item")[0].children;
let index = -1;
for (let i = 0; i < allChildren.length; i++) {
const textContent = allChildren[i].textContent;
if (textContent.indexOf(this.searchText) > -1) {
index = i;
break;
}
}
if (index >= 0) {
const currentTop = allChildren[index].offsetTop;
if (this.chapterContentHeight > this.docmHeight) {
let maxTop = this.chapterContentHeight - this.docmHeight;
if (currentTop >= maxTop) {
$("#chapter-content-list .show").css({
transform: `translateY(-${maxTop}px)`,
WebkitTranform: `translateY(-${maxTop}px)`,
});
} else {
$("#chapter-content-list .show").css({
transform: `translateY(-${currentTop}px)`,
WebkitTranform: `translateY(-${currentTop}px)`,
});
}
}
}
}
},
// 气泡操作
async tooltipOpa(elem) {
const position = $(elem).data("random");
this.tooltipId = position;
let url = `${this.baseURL}${checkTooptipInPosition}`;
const data = await request.post(url, {
book_id: this.book_id,
chapter_id: this.chapter_id,
position: position,
});
if (data.code === 200) {
this.tooltipContent = data.data;
$(".tange-tooptip").css({ zIndex: 1230, display: "block" }).find(".square").removeClass('square_bottom square_top');
setTimeout(() => {
this.calcPosition($(elem), $(".tange-tooptip"));
}, 50);
}
},
// 交互练习
async practiceOpa(elem) {
// 交互练习
const position = $(elem).data("practicenum");
const title = $(elem).data("title");
if (position) {
let url = `${this.baseURL}${checkPracticeInPosition}`;
const data = await request.post(url, {
book_id: this.book_id,
chapter_id: this.chapter_id,
position: position,
});
if (data.code === 200) {
this.callHandlerNoticeFlutterSubmit("answerResultCallBack", [
this.chapter_id,
position,
data.data.counts,
title,
]);
}
}
},
// 内容加载完成事件
async waitForContentLoad() {
const _that = this;
return new Promise((resolve) => {
const observer = new MutationObserver(() => {
// 检查内容是否加载完成(你可能需要根据实际情况调整条件)
if (document.readyState === "complete") {
let loadedCount = 0;
const images = document
.querySelector("#chapter-item")
.querySelectorAll("img");
if (images.length > 0) {
images.forEach((image) => {
image.addEventListener("load", function () {
loadedCount++;
// 如果所有图片都加载完成,执行其他操作
if (loadedCount === images.length) {
_that.chapterContentHeight = $("#chapter-content-list .show").innerHeight();
observer.disconnect();
resolve();
}
});
image.addEventListener("error", function () {
loadedCount++;
// 处理加载失败的情况
if (loadedCount === images.length) {
_that.chapterContentHeight = $("#chapter-content-list .show").innerHeight();
observer.disconnect();
resolve();
}
});
});
} else {
_that.chapterContentHeight = $("#chapter-content-list .show").innerHeight();
observer.disconnect();
resolve();
}
}
});
observer.observe(document, { childList: true, subtree: true });
});
},
// 监听touch事件
async listenHandlerEvents() {
const eleContent = document.querySelector("#chapter-content-list");
const chapterContent = document.querySelector("#chapter-content-list");
document.addEventListener("touchstart", this.touchStartEvent);
document.addEventListener("touchmove", this.touchMoveEvent);
document.addEventListener("touchend", this.touchEndEvent);
},
async touchStartEvent(event) {
event.stopPropagation();
const _that = this;
const target = event.target;
const selection = window.getSelection();
_that.touchstartTime = Date.now();
_that.touchIn = true;
cancelAnimationFrame(_that.animationId);
_that.animationType = null;
_that.velocity = 0.5;
if ($(target).closest(".tange-tooptip").length > 0 ) {
// event.preventDefault();
} else {
_that.tooltipId = null;
$(".tange-tooptip").css({ opacity: 0 });
}
// 使用正则表达式提取矩阵的最后两个值
const transformValue = $("#chapter-content-list .show").css(
"transform"
);
var matches = transformValue.match(
/matrix\(\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*\)/
);
_that.translateY = matches[6] ? parseInt(matches[6]) : 0;
_that.scrollPageY =
event.pageY ||
event.changedTouches[0].clientY ||
event.touches[0].clientY;
_that.scrollLoading = true;
},
async touchMoveEvent(event) {
event.stopPropagation();
const _that = this;
const target = event.target;
if (_that.galleryIndex > 0) {
event.preventDefault();
return false;
}
if ($(target).closest(".tange-tooptip").length > 0 ) {
event.preventDefault();
return false;
}
const end =
event.pageY ||
event.changedTouches[0].clientY ||
event.touches[0].clientY;
const move = end - _that.scrollPageY;
if (_that.selectedText) return;
if (_that.touchIn) {
const endY =
event.pageY ||
event.changedTouches[0].clientY ||
event.touches[0].clientY;
const move = endY - _that.scrollPageY;
const lateY = _that.translateY + parseInt(move / 1);
$("#chapter-content-list .show").css({
transform: `translateY(${lateY}px)`,
WebkitTranform: `translateY(${lateY}px)`,
});
const mayScrollHeight = _that.chapterContentHeight - _that.docmHeight; // 可滚动的高
if (lateY > 0) {
// 触发下拉刷新
let opacity = 0;
if (lateY >= 100) {
opacity = 1;
} else if (lateY <= 50) {
opacity = 0;
} else {
opacity = (lateY / 50).toFixed(2);
}
$(".prev-chapter").css({ opacity: opacity });
} else if (lateY <= 0 && mayScrollHeight - Math.abs(lateY) >= 0) {
$(".prev-chapter, .next-chapter").css({ opacity: 0 });
} else if (Math.abs(lateY) - mayScrollHeight > 0) {
// 触发上拉加载
let maxLateY = Math.abs(lateY) - mayScrollHeight;
if (maxLateY >= 100) {
opacity = 1;
} else if (lateY <= 50) {
opacity = 0;
} else {
opacity = (maxLateY / 50).toFixed(2);
}
$(".next-chapter").css({ opacity: opacity });
}
}
},
async touchEndEvent(event) {
event.stopPropagation();
const _that = this;
const target = event.target;
const endTime = Date.now();
const endY =
event.pageY ||
event.changedTouches[0].clientY ||
event.touches[0].clientY;
// 处理双击事件
if (
_that.touchLastClickTime &&
endTime - _that.touchLastClickTime < 300
) {
_that.touchLastClickTime = null;
_that.callHandlerNoticeFlutterSubmit(`dbClickCallBack`);
_that.initArags();
_that.clearRangeContent();
event.preventDefault();
return false;
}
_that.touchLastClickTime = endTime;
_that.touchIn = false;
if ($(target).closest(".tange-tooptip").length > 0 ) {
const href = $(target).closest(".c-link").attr("href");
_that.callHandlerNoticeFlutterSubmit(
"openLinkCallback",
href
);
_that.tooltipId = null;
event.preventDefault();
return false;
}
const selection = window.getSelection();
const range = document.createRange();
if (
target.nodeName.toLowerCase() === "line" ||
target.nodeName.toLowerCase() === "light" ||
$(target).closest(".controll-position").length > 0 ||
$(target).closest(".controll-color-list").length > 0
) {
if (_that.tempCloneRange) {
selection.removeAllRanges();
selection.addRange(_that.tempCloneRange);
} else {
_that.initArags();
_that.clearRangeContent();
}
} else {
_that.initArags();
_that.clearRangeContent();
}
// 上一章,下一章
const move = endY - _that.scrollPageY;
const lateY = _that.translateY + parseInt(move / 2);
const mayScrollHeight = _that.chapterContentHeight - _that.docmHeight; // 可滚动的高
if (endTime - _that.touchstartTime <= 300) {
// 创建 Range 对象
const range = document.createRange();
const selection = window.getSelection();
if (Math.abs(move) <= 30) {
// 点击事件
if (target.nodeName.toLowerCase() === "span") {
// 点击的气泡内容
if ($(target).closest(".chapter-item-tooltip").length > 0) {
_that.tooltipOpa($(target).closest(".chapter-item-tooltip"));
event.preventDefault();
} else if ($(target).closest(".colorLine").length > 0) {
this.boolControllStatus = false;
this.boolLightColor = true;
event.preventDefault();
} else if ($(target).closest("a").length > 0) {
const href = $(target).closest("a").attr("href");
_that.callHandlerNoticeFlutterSubmit(
"openLinkCallback",
href
);
event.stopPropagation();
event.preventDefault();
} else if ($(target).closest(".chapter-item-link").length) {
// 链接
const title = $(target).closest(".chapter-item-link").data("title");
const position = $(target).closest(".chapter-item-link").data("random");
const linktype = $(target).closest(".chapter-item-link").data("linktype");
const href = $(target).closest(".chapter-item-link").data("link");
const chapters = $(target).closest(".chapter-item-link").data("chapters");
if (parseInt(linktype) === 1) {
// 链接设置
_that.callHandlerNoticeFlutterSubmit(
"openLinkCallback",
href
);
} else {
// 章节
let last = null;
if (isNaN(chapters) && chapters.indexOf(',') > -1) {
const arr = chapters.split(',');
last = arr[arr.length - 1];
} else {
last = chapters
}
_that.chapter_id = last;
_that.callHandlerNoticeFlutterSubmit(
"loadChapterCallBack",
[_that.chapter_id, title]
);
await _that.getBookChapterData();
}
event.stopPropagation();
event.preventDefault();
} else if ($(target).closest(".lock-button").length) {
_that.callHandlerNoticeFlutterSubmit('payCallback');
event.stopPropagation();
event.preventDefault();
}
} else if (target.nodeName.toLowerCase() === "a") {
const href = $(target).attr("href");
_that.callHandlerNoticeFlutterSubmit(
"openLinkCallback",
href
);
event.stopPropagation();
event.preventDefault();
} else if (target.nodeName.toLowerCase() === "use" || target.nodeName.toLowerCase() === "svg") {
if ($(target).closest(".chapter-item-link").length) {
// 链接
const title = $(target).closest(".chapter-item-link").data("title");
const position = $(target).closest(".chapter-item-link").data("random");
const linktype = $(target).closest(".chapter-item-link").data("linktype");
const href = $(target).closest(".chapter-item-link").data("link");
const chapters = $(target).closest(".chapter-item-link").data("chapters");
if (parseInt(linktype) === 1) {
// 链接设置
_that.callHandlerNoticeFlutterSubmit(
"openLinkCallback",
href
);
} else {
// 章节
let last = null;
if (isNaN(chapters) && chapters.indexOf(',') > -1) {
const arr = chapters.split(',');
last = arr[arr.length - 1];
} else {
last = chapters
}
_that.chapter_id = last;
_that.callHandlerNoticeFlutterSubmit(
"loadChapterCallBack",
[_that.chapter_id, title]
);
await _that.getBookChapterData();
}
} else if ($(target).closest(".chapter-gallery-container").length) {
// 画廊
const title = $(target).closest(".chapter-gallery-container").data("title");
const position = $(target).closest(".chapter-gallery-container").data("random");
_that.callHandlerNoticeFlutterSubmit("readInfoCallback", [
position,
"gallery",
title,
]);
} else if ($(target).closest(".chapter-item-tooltip").length > 0) {
_that.tooltipOpa(
$(target).closest(".chapter-item-tooltip")
);
} else if ($(target).closest(".chapter-expand").length > 0) {
// 扩展
const title = $(target).closest(".chapter-expand").data("name");
const position = $(target).closest(".chapter-expand").data("random");
_that.callHandlerNoticeFlutterSubmit("readInfoCallback", [
position,
"expand",
title,
]);
} else if ($(target).closest(".c-link").length > 0) {
// 气泡链接
const href = $(target).closest(".c-link").attr("href");
_that.callHandlerNoticeFlutterSubmit(
"openLinkCallback",
href
);
}
} else if (target.nodeName.toLowerCase() === "img") {
// 点击的气泡内容
if ($(target).closest(".chapter-item-tooltip").length > 0) {
_that.tooltipOpa(
$(target).closest(".chapter-item-tooltip")
);
} else if (
$(target).closest(".chapter-gallery-container").length
) {
// 画廊
const title = $(target)
.closest(".chapter-gallery-container")
.data("title");
const position = $(target)
.closest(".chapter-gallery-container")
.data("random");
_that.callHandlerNoticeFlutterSubmit("readInfoCallback", [
position,
"gallery",
title,
]);
} else {
// 图片展示
const scaleImg = $(target).attr("src");
const title = $(target).attr("alt") || "图片展示";
_that.callHandlerNoticeFlutterSubmit("scaleImageCallback", [
scaleImg,
title,
]);
}
event.stopPropagation();
event.preventDefault();
} else {
if ($(target).closest(".chapter-practice").length > 0) {
const ele = $(target).closest(".chapter-practice");
_that.practiceOpa(ele);
event.preventDefault();
} else if (target.nodeName.toLowerCase() === "line") {
let className = target.getAttribute("class");
_that.nowLineId = target.getAttribute("data-line-id");
_that.nowColorId = -1;
const all = document.querySelectorAll(`.${className}`);
_that.side = all[0].getBoundingClientRect();
_that.boolControllStatus = true;
_that.boolLightColor = false;
await _that.clickLineAndLightContent(all, target, 'line');
_that.lineAndLight = true;
// _that.calcPositionRange();
event.preventDefault();
} else if (target.nodeName.toLowerCase() === "light") {
let className = target.getAttribute("class");
_that.nowColorId = target.getAttribute("data-light-id");
_that.nowLineId = -1;
let classNameArr = className.split(" ");
let firstClassName = classNameArr[0];
const all = document.querySelectorAll(`.${firstClassName}`);
_that.nowActiveColor = classNameArr[1].replace("c-", "");
_that.side = all[0].getBoundingClientRect();
_that.boolControllStatus = true;
_that.boolLightColor = false;
await _that.clickLineAndLightContent(all, target, 'light');
_that.lineAndLight = true;
// _that.calcPositionRange();
event.preventDefault();
} else if (
$(target).closest(".com.prev").length > 0 ||
$(target).closest(".com.next").length > 0
) {
// 点击左右标志,保持
_that.rangeMenuStatus = true;
if ($(target).closest(".com.prev").length > 0) {
$(".controll-position .controll-list").css({
transform: "translateX(0px)",
WebkitTranform: `translateX(0px)`,
});
_that.rangeMenuStatus = false;
}
if ($(target).closest(".com.next").length > 0) {
let cWidth = 0;
$(
".controll-position .controll-list .controll-item"
).each((index, item) => {
cWidth += parseInt($(item).innerWidth());
});
const pWidth = $(".controll-container").innerWidth();
const eWidth = $(".controll-container .com").innerWidth();
const lateX = cWidth - pWidth + eWidth;
$(".controll-position .controll-list").css({
transform: `translateX(-${lateX}px)`,
WebkitTranform: `translateX(-${lateX}px)`,
});
_that.rangeMenuStatus = true;
}
event.preventDefault();
} else if ($(target).closest(".controll-item").length > 0) {
if (_that.boolLightColor) return;
// 操作
const ele = $(target).closest(".controll-item");
if ($(ele).find(".opa").length > 0) {
let classList = $(ele).find(".opa")[0].classList;
if (
["discuss", "note", "baike", "dict", "read"].includes(
classList[1]
)
) {
_that.rangeTextInFlutter(classList[1]);
} else if (classList[1] === "line") {
_that.rangeTextInLine();
} else if (classList[1] === "light") {
_that.rangeTextInLight();
}
}
event.stopPropagation();
event.preventDefault();
} else if (
$(target).closest(".controll-color-item").length > 0 ||
$(target).hasClass("controll-color-item")
) {
// 颜色
const index = $(target).closest(".controll-color-item").index();
_that.setLightColorText(_that.colorList[index]);
event.stopPropagation();
event.preventDefault();
} else if (
$(target).closest(".chapter-gallery-container").length
) {
// 画廊
const title = $(target)
.closest(".chapter-gallery-container")
.data("title");
const position = $(target)
.closest(".chapter-gallery-container")
.data("random");
_that.callHandlerNoticeFlutterSubmit("readInfoCallback", [
position,
"gallery",
title,
]);
event.stopPropagation();
event.preventDefault();
} else if ($(target).closest(".chapter-expand").length) {
// 扩展
const title = $(target).closest(".chapter-expand").data("name");
const position = $(target).closest(".chapter-expand").data("random");
_that.callHandlerNoticeFlutterSubmit("readInfoCallback", [
position,
"expand",
title,
]);
event.stopPropagation();
event.preventDefault();
} else if ($(target).closest(".tange-tooptip").length) {
console.log(12313);
console.log(_that.tooltipId);
// 气泡
event.stopPropagation();
event.preventDefault();
} else {
_that.tooltipId = null;
}
}
}
if (Math.abs(move) > 0) {
if (move > 0) {
_that.animationType = "down";
} else if (move < 0) {
_that.animationType = "up";
}
_that.animationScroll();
}
$(".prev-chapter, .next-chapter").css({ opacity: 0 });
} else {
if (_that.selectedText) return;
if (lateY > 0) {
if (move >= 200) {
// 下拉刷新开始
if (Object.entries(_that.prev_chapter).length > 0) {
$(".prev-chapter").css({ opacity: 0 });
_that.chapter_id = _that.prev_chapter.id;
_that.callHandlerNoticeFlutterSubmit(
"loadChapterCallBack",
[_that.chapter_id, _that.prev_chapter.name]
);
await _that.getBookChapterData(null, true);
} else {
$(".prev-chapter").css({ opacity: 0 });
$("#chapter-content-list .show").css({
transform: `translateY(${0}px)`,
WebkitTranform: `translateY(${0}px)`,
});
}
} else {
$(".prev-chapter").css({ opacity: 0 });
$("#chapter-content-list .show").css({
transform: `translateY(${0}px)`,
WebkitTranform: `translateY(${0}px)`,
});
}
} else if (lateY < 0) {
if (Math.abs(lateY) >= mayScrollHeight) {
clearInterval(_that.readTimer);
_that.uploadReadProgress(null, 100);
_that.uploadReadRecord(null, 100);
_that.uploadReadInterval();
if (Math.abs(move) >= 200) {
if (Object.entries(_that.next_chapter).length > 0) {
$(".next-chapter").css({ opacity: 0 });
_that.chapter_id = _that.next_chapter.id;
_that.callHandlerNoticeFlutterSubmit(
"loadChapterCallBack",
[_that.chapter_id, _that.next_chapter.name]
);
await _that.getBookChapterData();
}
} else {
$(".next-chapter").css({ opacity: 0 });
$("#chapter-content-list .show").css({
transform: `translateY(-${mayScrollHeight}px)`,
WebkitTranform: `translateY(-${mayScrollHeight}px)`,
});
}
}
} else {
$("#chapter-content-list .show").css({
transform: `translateY(${0}px)`,
WebkitTranform: `translateY(${0}px)`,
});
}
_that.scrollLoading = false;
}
// 防抖函数
_that.selectRangeTimeout = setTimeout(async () => {
if (!_that.selectedText) {
_that.initArags();
_that.clearRangeContent();
} else {
const selection = window.getSelection();
const range = selection.getRangeAt(0);
if (_that.boolLightColor) {
_that.boolControllStatus = false;
} else {
_that.boolControllStatus = true;
}
}
}, 150);
},
// 缓冲动画设置
animationScroll() {
if (!this.touchIn) {
const transformValue = $("#chapter-content-list .show").css(
"transform"
);
var matches = transformValue.match(
/matrix\(\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*\)/
);
let translateY = matches[6] ? parseInt(matches[6]) : 0;
// 根据摩擦系数逐渐减小速度
this.velocity *= 1 - this.friction;
let tempY = 0;
if (this.animationType === "up") {
tempY = translateY - this.velocity * 75;
} else if (this.animationType === "down") {
tempY = translateY + this.velocity * 75;
}
if (tempY > 0) {
tempY = 0;
} else if (
Math.abs(tempY) >=
this.chapterContentHeight - this.docmHeight
) {
tempY = -(this.chapterContentHeight - this.docmHeight);
}
$("#chapter-content-list .show").css({
transform: `translateY(${tempY}px)`,
WebkitTranform: `translateY(${tempY}px)`,
});
// 如果速度足够小,停止动画
if (Math.abs(this.velocity) <= 0.1) {
cancelAnimationFrame(this.animationId);
this.animationType = null;
this.velocity = 0.6;
return;
}
// 执行下一帧动画
this.animationId = requestAnimationFrame(this.animationScroll);
}
},
// range 设置
async selectionRangeOperation() {
const _that = this;
document.addEventListener("selectionchange", async () => {
const selection = window.getSelection();
if (selection && selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
const selectedText = range.toString();
if (selectedText.trim() != "") {
this.side = range.getBoundingClientRect();
if (_that.boolLightColor) {
if (_that.nowColorId > -1) {
_that.boolLightColor = true;
_that.boolControllStatus = false;
}
} else {
_that.boolLightColor = false;
_that.boolControllStatus = true;
}
// 保持选区
_that.tempCloneRange = range.cloneRange();
_that.selectedText = selectedText;
this.checkRangeInfo();
} else {
_that.initArags();
}
}
});
},
async checkRangeInfo() {
const selection = window.getSelection();
const range = selection.getRangeAt(0);
const selectedText = range.toString();
const startContainer = range.startContainer;
const endContainer = range.endContainer;
const commonAncestor = range.commonAncestorContainer;
const fragment = range.cloneContents();
const nodesChild = Array.from(fragment.childNodes);
// 当前章节
const id = `chapter-item`;
const parentNodeMax = document.querySelector(`#${id}`);
let oneStartOffset = -1;
let oneEndOffset = -1;
let lastStartOffset = -1;
let lastEndOffset = -1;
let rangeInformations = [];
// 判断是否是多行选区
if ($(commonAncestor).hasClass("chapter-item")) {
const elementFirst = await this.getParentNode(startContainer);
const elementLast = await this.getParentNode(endContainer);
// 获取中间的行
const allParagraphs = await this.getParagraphsBetween(elementFirst, elementLast);
this.selectRangeType = 'more';
// 多行选区
const parentStartNode = startContainer.parentNode;
const parentEndNode = endContainer.parentNode;
const firstParentNodeName = parentStartNode.nodeName.toLowerCase();
const lastParentNodeName = parentEndNode.nodeName.toLowerCase();
if (['line', 'light'].includes(firstParentNodeName) && ['line', 'light'].includes(lastParentNodeName)) {
oneStartOffset = $(parentStartNode).data("startoffset");
lastEndOffset = $(parentEndNode).data("endoffset");
} else if (['line', 'light'].includes(firstParentNodeName) && endContainer.nodeType === 3) {
const endText = endContainer.textContent;
const endElement = endContainer.previousElementSibling;
if (endElement) {
startOffset = 0;
if (['line', 'light'].includes(endElement.nodeName.toLowerCase())) {
endOffset = $(endElement).data("endoffset") + range.endOffset;
}
} else {
startOffset = 0;
endOffset = range.endOffset;
}
} else if (['line', 'light'].includes(lastParentNodeName) && startContainer.nodeType === 3) {
const startElement = startContainer.previousElementSibling;
const startText = startContainer.textContent;
const startIndex = parentStartNode.textContent.indexOf(startElement.textContent);
oneStartOffset = startIndex + range.startOffset;
oneEndOffset = parentStartNode.textContent.length;
lastStartOffset = 0;
lastEndOffset = $(parentEndNode).data("endoffset");
} else {
oneStartOffset = parentStartNode.textContent.indexOf(nodesChild[0].innerText)
oneEndOffset = parentStartNode.textContent.length;
lastStartOffset = 0;
lastEndOffset = nodesChild[nodesChild.length - 1].innerText.length;
}
allParagraphs.forEach(async (item, index) => {
const sideIndex = Array.from(parentNodeMax.children).indexOf(item);
if (index === 0) {
rangeInformations[index] = {
startOffset: oneStartOffset,
endOffset: oneEndOffset,
sideIndex: sideIndex,
parentIdBook: id,
parentTagName: item.nodeName.toLowerCase(),
};
} else if (index === nodesChild.length - 1) {
rangeInformations[index] = {
startOffset: 0,
endOffset: lastEndOffset,
sideIndex: sideIndex,
parentIdBook: id,
parentTagName: item.nodeName.toLowerCase(),
};
} else {
rangeInformations[index] = {
startOffset: 0,
endOffset: item.textContent.length,
sideIndex: sideIndex,
parentIdBook: id,
parentTagName: item.nodeName.toLowerCase(),
};
}
})
} else {
this.selectRangeType = 'line';
let simpleLineText;
const parentNode = await this.getParentNode(startContainer);
const sideIndex = Array.from(parentNodeMax.children).indexOf(parentNode);
if (commonAncestor.nodeType === 3) {
simpleLineText = parentNode.textContent;
} else {
simpleLineText = commonAncestor.textContent;
}
const maxLength = countSubstringOccurrences(simpleLineText, selectedText);
const firstChild = nodesChild[0];
const lastChild = nodesChild[nodesChild.length - 1];
const firstNodeName = firstChild.nodeName.toLowerCase();
const lastNodeName = lastChild.nodeName.toLowerCase();
if (maxLength > 1) {
const nodeValue = commonAncestor.textContent;
const index = nodeValue.indexOf(selectedText);
const previousSibling = commonAncestor.previousSibling;
if (nodesChild.length > 1) {
// 首尾都是划线或高亮的内容
if (["line", "light"].includes(firstNodeName) && ["line", "light"].includes(lastNodeName) ) {
oneStartOffset = $(firstChild).data("startoffset");
endOffset = $(lastChild).data("endoffset");
} else if (["line", "light"].includes(firstNodeName) && lastChild.nodeType === 3) {
oneStartOffset = $(firstChild).data("startoffset");
const oneText = endContainer.textContent;
const tempstart = simpleLineText.indexOf(oneText);
oneEndOffset = tempstart + lastChild.textContent.length;
} else if (["line", "light"].includes(lastNodeName) && firstChild.nodeType === 3) {
const oneText = startContainer.textContent;
const tempstart = simpleLineText.indexOf(oneText);
oneStartOffset = tempstart + range.startOffset;
oneEndOffset = $(lastChild).data("endoffset");
} else {
oneStartOffset = simpleLineText.indexOf(selectedText);
oneEndOffset = oneStartOffset + selectedText.length;
}
} else {
// 单行中多个相同, 但是选择的文本只有一个
const oneText = startContainer.textContent;
const tempstart = simpleLineText.indexOf(oneText);
oneStartOffset = tempstart + range.startOffset;
oneEndOffset = oneStartOffset + selectedText.length;
}
} else {
// 只找到这个
if (["line", "light"].includes(firstNodeName) && ["line", "light"].includes(lastNodeName) ) {
oneStartOffset = $(firstChild).data("startoffset");
oneEndOffset = $(lastChild).data("endoffset");
} else if (["line", "light"].includes(firstNodeName) && lastChild.nodeType === 3) {
oneStartOffset = $(firstChild).data("startoffset");
const cStart = simpleLineText.indexOf(selectedText);
oneEndOffset = cStart + selectedText.length;
} else if (["line", "light"].includes(lastNodeName) && firstChild.nodeType === 3) {
oneStartOffset = simpleLineText.indexOf(selectedText);
oneEndOffset = $(lastChild).data("endoffset");
} else {
oneStartOffset = simpleLineText.indexOf(selectedText);
oneEndOffset = oneStartOffset + selectedText.length;
}
}
rangeInformations = [{
startOffset: oneStartOffset,
endOffset: oneEndOffset,
parentTagName: parentNode.nodeName.toLowerCase(),
parentIdBook: id,
sideIndex: sideIndex,
}];
}
let lightIds = [];
let lineIds = [];
nodesChild.forEach((item, index) => {
const itemNodeName = item.nodeName.toLocaleLowerCase();
if (item.nodeType === 1 && ['light', 'line'].includes(itemNodeName)) {
const id = $(item).data('id');
var attributes = item.attributes;
var attributeList = {};
attributeList.tagName = itemNodeName;
attributeList.innerText = item.textContent;
for (var j = 0; j < attributes.length; j++) {
var attributeName = attributes[j].name;
var attributeValue = attributes[j].value;
attributeList[attributeName] = attributeValue;
}
if (itemNodeName === 'light') {
lightIds.push(attributeList);
} else if (itemNodeName === 'line') {
lineIds.push(attributeList);
}
}
})
nodesChild.forEach((item, index) => {
const itemNodeName = item.nodeName.toLocaleLowerCase();
if (item.nodeType === 1 && itemNodeName === 'light') {
this.nowActiveColor = $(item).attr("class").split(' ')[1].replace('c-', '');
}
})
this.expandLine = lineIds;
this.expandLight = lightIds;
this.rangePosition = { type: this.selectRangeType, info: rangeInformations };
},
// 点击讨论、笔记、百科
async rangeTextInFlutter(type) {
if (this.selectedText && type) {
this.callHandlerNoticeFlutterSubmit(
`${type}CallBack`,
this.selectedText
);
}
},
// 点击颜色设置-颜色菜单
openColorChange(ev) {
this.boolControllStatus = false;
this.boolLightColor = true;
ev.stopPropagation();
ev.preventDefault();
},
rangeMenuMove(type, ev) {
console.log(type);
ev.stopPropagation();
ev.preventDefault();
},
// 划线
async rangeTextInLine(ev) {
const selection = window.getSelection();
if (this.expandLine.length > 0) {
// // 删除里面的多余的划线
if (this.expandLine.length === 1) {
const id = this.expandLine[0]["id"];
const nowLineId = this.expandLine[0]["data-id"];
await this.replaceWithElement(`${id}`);
this.expandIds = nowLineId;
} else if (this.expandLine.length > 1) {
const ids = [];
this.expandLine.forEach(async (item) => {
ids.push(item["data-id"]);
await this.replaceWithElement(`${item["id"]}`);
});
this.expandIds = ids;
}
}
if (this.nowLineId > -1) {
const res = await this.delNotes("line");
if (res.is_success === 1) {
const range = document.createRange();
// 这是去除
if (this.rangePosition.type === "line") {
const element = document.getElementById(
`line-none-${this.nowLineId}-0`
);
if (element) {
range.selectNode(element);
selection.removeAllRanges();
selection.addRange(range);
await this.replaceWithElement(
`line-none-${this.nowLineId}-0`
);
}
} else {
const elements = document.querySelectorAll(
`.line-none-${this.nowLineId}`
);
if (elements) {
elements.forEach(async (item) => {
const id = $(item).attr("id");
await this.replaceWithElement(`${id}`);
});
}
}
this.nowLineId = -1;
}
return;
}
const data = await this.addNotes(1);
if (!data.id) return;
this.nowLineId = data.id;
this.boolControllStatus = true;
this.boolLightColor = false;
this.findSelectionRangeFill("line");
// 删除之前的
this.delOldLineAndLightIds();
},
// 高亮设置
async rangeTextInLight() {
const selection = window.getSelection();
this.boolControllStatus = false;
this.boolLightColor = true;
if (this.expandLight.length > 0) {
// 删除里面的多余的高亮
if (this.expandLight.length === 1) {
const id = this.expandLight[0]["id"];
const nowLineId = this.expandLight[0]["data-id"];
await this.replaceWithElement(`${id}`);
this.expandIds = nowLineId;
} else if (this.expandLight.length > 1) {
const ids = [];
this.expandLight.forEach(async (item) => {
ids.push(item["data-id"]);
await this.replaceWithElement(`${item["id"]}`);
});
this.expandIds = ids;
}
}
if (this.nowColorId > -1) {
this.boolLightColor = false;
this.boolControllStatus = true;
const res = await this.delNotes("light");
if (res.is_success === 1) {
const range = document.createRange();
// 这是去除
if (this.rangePosition.type === "line") {
const element = document.getElementById(
`light-none-${this.nowColorId}-0`
);
if (element) {
range.selectNode(element);
selection.removeAllRanges();
selection.addRange(range);
await this.replaceWithElement(
`light-none-${this.nowColorId}-0`
);
this.nowColorId = -1;
}
} else {
const elements = document.querySelectorAll(
`.light-none-${this.nowColorId}`
);
if (elements) {
elements.forEach(async (item) => {
const id = $(item).attr("id");
await this.replaceWithElement(`${id}`);
});
}
}
this.nowColorId = -1;
this.nowActiveColor = false;
}
return;
} else {
if (!this.nowActiveColor) {
this.setLightColorText(this.colorList[0], true);
} else {
this.setLightColorText(this.nowActiveColor, true);
}
}
},
// 颜色切换设置 - 按钮
rangeTextChangeLight(ev) {
this.boolControllStatus = false;
this.boolLightColor = true;
ev.stopPropagation();
ev.preventDefault();
},
// 设置高亮
async setLightColorText(color, bool) {
this.nowActiveColor = color;
let data = null;
this.boolControllStatus = false;
this.boolLightColor = true;
if (bool) {
data = await this.addNotes(2);
if (!data.id) return;
this.nowColorId = data.id;
this.findSelectionRangeFill("light");
// 删除之前的
this.delOldLineAndLightIds();
return;
} else {
data = await this.editNotes(2);
if (data.is_success != 1) return;
const all = document.querySelectorAll(`light.light-none-${this.nowColorId}`);
all.forEach((item) => {
$(item).removeClass().addClass(`light-none-${this.nowColorId} c-${color}`);
});
this.nowActiveColor = color;
}
},
// 划线高亮公用查找选区
findSelectionRangeFill(type) {
const selection = window.getSelection();
const selectedRange = selection.getRangeAt(0);
let tempNewRange = [];
let newPosition = this.rangePosition;
newPosition.info.forEach((item, index) => {
// 创建什么类型的元素
const span = document.createElement(type);
const element = document.querySelector(`#${item.parentIdBook}`).children[item.sideIndex];
let textContent = element.textContent;
let textHtml = element.innerHTML;
let text = textContent.substring(item.startOffset, item.endOffset);
span.innerHTML = text;
let id = '';
if (type === "line") {
span.setAttribute("data-line-id", this.nowLineId);
span.setAttribute("class", `line-none-${this.nowLineId}`);
id = `line-none-${this.nowLineId}-${index}`;
span.setAttribute("id", id);
} else if (type === "light") {
span.setAttribute("data-light-id", this.nowColorId);
span.setAttribute("class",`light-none-${this.nowColorId} c-${this.nowActiveColor}`);
id = `light-none-${this.nowColorId}-${index}`;
span.setAttribute("id", id);
}
span.setAttribute("data-startoffset", item.startOffset);
span.setAttribute("data-endoffset", item.endOffset);
span.setAttribute("data-parentTagName", item.parentTagName);
span.setAttribute("data-sideIndex", item.sideIndex);
const rangeItem = document.createRange();
if (newPosition.info.length === 1) {
selection.removeAllRanges();
if (this.expandIds) {
const newTextHtml = textHtml.replace(text, span.outerHTML);
element.innerHTML = newTextHtml;
const idElement = document.querySelector(`#${id}`);
const tempRange = document.createRange();
tempRange.selectNode(idElement);
selection.addRange(tempRange);
} else {
selectedRange.surroundContents(span);
selection.addRange(selectedRange);
}
} else {
const lineArr = element.querySelectorAll("line");
const lightArr = element.querySelectorAll("light");
if (lineArr.length === 0 && lightArr.length === 0) {
const beforeText = textContent.substring(0, item.startOffset);
const endText = textContent.substring(item.endOffset);
$(element).html(beforeText + span.outerHTML + endText);
} else {
const newHtml = textHtml.replace(text, span.outerHTML);
$(element).html(newHtml);
}
tempNewRange.push(span);
}
});
// 遍历每个单独的范围并调整组合范围
if (newPosition.type === "more") {
const lineRange1 = document.createRange();
const lineRange2 = document.createRange();
if (type === "line") {
lineRange1.selectNodeContents(
document.getElementById(`line-none-${this.nowLineId}-0`)
);
lineRange2.selectNodeContents(
document.getElementById(
`line-none-${this.nowLineId}-${tempNewRange.length - 1}`
)
);
} else if (type === "light") {
lineRange1.selectNodeContents(
document.getElementById(`light-none-${this.nowLineId}-0`)
);
lineRange2.selectNodeContents(
document.getElementById(
`light-none-${this.nowLineId}-${tempNewRange.length - 1}`
)
);
}
// 合并选区
var mergedRange = document.createRange();
mergedRange.setStartBefore(lineRange1.startContainer); // 设置起始位置为range1的开头之前
mergedRange.setEndAfter(lineRange2.endContainer); // 设置结束位置为range2的结尾之后
// 将合并后的范围应用到文档的选区中
selection.removeAllRanges();
selection.addRange(mergedRange);
}
},
// 清空选区
clearRangeContent() {
var selection = window.getSelection();
selection.removeAllRanges();
this.tempCloneRange = null;
},
// 添加划线、高亮
async addNotes(type) {
let positioning = JSON.stringify(this.rangePosition);
let url = `${this.baseURL}${addInfomationNotes}`;
let data = {
book_id: this.book_id,
chapter_id: this.chapter_id,
types: type,
color: type === 2 ? `#${this.nowActiveColor}` : "#ab1941",
content: this.selectedText,
positioning: positioning,
};
const res = await request.post(url, data);
if (res.code === 200) {
this.callHandlerNoticeFlutterSubmit(`offlineAddNoteCallBack`, { ...data, notes_id: res.data.id});
return res.data;
}
},
// 编辑划线、高亮
async editNotes(type, expand, notes_id) {
let id = null;
if (expand) {
id = notes_id;
} else {
id = type === 2 ? this.nowColorId : this.nowLineId;
}
let url = `${this.baseURL}${editInfomationNotes}`;
let data = {
book_id: this.book_id,
chapter_id: this.chapter_id,
notes_id: id,
types: type,
color: type === 2 ? `#${this.nowActiveColor}` : "",
content: this.selectedText,
positioning: JSON.stringify(this.rangePosition),
};
const res = await request.post(url, data);
if (res.code === 200) {
this.callHandlerNoticeFlutterSubmit(`offlineUpdateNoteCallBack`, { ...data });
return res.data;
}
},
// 删除划线、高亮
async delNotes(type, id) {
let url = `${this.baseURL}${delInfomationNotes}`;
let data = {
book_id: this.book_id,
chapter_id: this.chapter_id,
notes_id: id
? id
: type === "line"
? this.nowLineId
: this.nowColorId,
};
const res = await request.post(url, data);
this.callHandlerNoticeFlutterSubmit(`offlineDelNoteCallBack`, { ...data });
return res.data;
},
// 批量删除划线、高亮
async delBatchNotes(notes) {
let url = `${this.baseURL}${delBatchInfomationNotes}`;
let data = {
book_id: this.book_id,
chapter_id: this.chapter_id,
notes_ids: notes.join(","),
};
const res = await request.post(url, data);
this.callHandlerNoticeFlutterSubmit(`offlineDelNoteCallBack`, { ...data });
return res.data;
},
// 删除之前的内容
async delOldLineAndLightIds () {
const ids = [];
if (this.expandLine.length > 0) {
this.expandLine.forEach(async (item) => {
ids.push(item["data-id"]);
});
}
if (this.expandLight.length > 0) {
this.expandLight.forEach(async (item) => {
ids.push(item["data-id"]);
});
}
if (ids.length > 0) {
await this.delBatchNotes(ids);
}
this.expandIds = null;
},
// 点击划线或高亮的选择内容
async clickLineAndLightContent(all, target, type) {
const _that = this;
// 创建 Range 对象
const range = document.createRange();
const selection = window.getSelection();
if (all.length > 1) {
const node1 = document.getElementById(`${type}-none-${type === 'line' ? _that.nowLineId : _that.nowColorId }-0`);
const node2 = document.getElementById(`${type}-none-${type === 'line' ? _that.nowLineId : _that.nowColorId }-${all.length - 1}`);
const lineRange1 = document.createRange();
lineRange1.selectNodeContents(node1);
const lineRange2 = document.createRange();
lineRange2.selectNodeContents(node2);
var mergedRange = document.createRange();
mergedRange.setStartBefore(lineRange1.startContainer); // 设置起始位置为range1的开头之前
mergedRange.setEndAfter(lineRange2.endContainer); // 设置结束位置为range2的结尾之后
// 将合并后的范围应用到文档的选区中
selection.removeAllRanges();
selection.addRange(mergedRange);
let rangePosition = {
type: "more",
info: [],
};
all.forEach((item, index) => {
// 添加数据
let startOffset = item.getAttribute("data-startoffset");
let endOffset = item.getAttribute("data-endoffset");
let parentTagName = item.getAttribute("data-parentTagName");
let sideIndex = item.getAttribute("data-sideIndex");
let obj = {
startOffset: startOffset,
endOffset: endOffset,
parentTagName: parentTagName,
parentIdBook: `chapter-item`,
sideIndex: sideIndex,
};
rangePosition.info.push(obj);
});
_that.rangePosition = rangePosition;
_that.selectRangeType = "more";
} else {
// 添加数据
let startOffset = target.getAttribute("data-startoffset");
let endOffset = target.getAttribute("data-endoffset");
let parentTagName = target.getAttribute("data-parentTagName");
let sideIndex = target.getAttribute("data-sideIndex");
// range.setStart(target.firstChild, 0);
// range.setEnd(target.firstChild, endOffset - startOffset);
range.selectNodeContents(target);
// 用 Range 选中文本
selection.removeAllRanges();
selection.addRange(range);
let obj = {
startOffset: startOffset,
endOffset: endOffset,
parentTagName: parentTagName,
parentIdBook: `chapter-item`,
sideIndex: sideIndex,
};
let rangePosition = {
type: "line",
info: [obj],
};
_that.rangePosition = rangePosition;
_that.selectRangeType = "line";
}
// this.side = range.getBoundingClientRect();
_that.selectedText = range.toString();
},
// 回显划线和高亮
async line_listFilterShow() {
this.line_list.length &&
this.line_list.forEach((item) => {
try {
const positioning = JSON.parse(item.positioning);
const { type, info } = positioning;
if (type === "line") {
this.findTextAndReplaceText("line", info[0], item, 0);
} else if (type === "more") {
positioning.info.forEach((citem, index) => {
this.findTextAndReplaceText("line", citem, item, index);
});
}
} catch (e) {}
});
this.color_line.length &&
this.color_line.forEach((item) => {
try {
const positioning = JSON.parse(item.positioning);
const { type, info } = positioning;
if (type === "line") {
this.findTextAndReplaceText("light", info[0], item, 0);
} else if (type === "more") {
positioning.info.forEach((citem, index) => {
this.findTextAndReplaceText("light", citem, item, index);
});
}
} catch (e) {}
});
},
// 查找替换文本高亮设置
findTextAndReplaceText(type, citem, item, index) {
const itemInfo = citem;
const parentNode = document.querySelector(`#chapter-item`);
const allParagraphs = parentNode.children;
const targetParagraph = allParagraphs[itemInfo.sideIndex];
const textContent = targetParagraph.textContent;
const textHtml = targetParagraph.innerHTML;
const text = textContent.substring(
itemInfo.startOffset,
itemInfo.endOffset
);
const span = document.createElement(type);
span.innerHTML = text;
if (type === "line") {
span.setAttribute("data-line-id", item.line_id);
span.setAttribute("data-id", item.line_id);
span.setAttribute("class", `line-none-${item.line_id}`);
span.setAttribute("id", `line-none-${item.line_id}-${index}`);
itemInfo.line_id = item.line_id;
} else if (type === "light") {
span.setAttribute("data-light-id", item.color_id);
span.setAttribute("data-id", item.color_id);
span.setAttribute(
"class",
`light-none-${item.color_id} c-${item.color.replace("#", "")}`
);
span.setAttribute("id", `light-none-${item.color_id}-${index}`);
itemInfo.color_id = item.color_id;
}
span.setAttribute("data-startoffset", itemInfo.startOffset);
span.setAttribute("data-endoffset", itemInfo.endOffset);
span.setAttribute("data-parentTagName", itemInfo.parentTagName);
span.setAttribute("data-sideIndex", itemInfo.sideIndex);
const lineArr = targetParagraph.querySelectorAll("line");
const lightArr = targetParagraph.querySelectorAll("light");
if (lineArr.length === 0 && lightArr.length === 0) {
const beforeText = textContent.substring(0, itemInfo.startOffset);
const endText = textContent.substring(itemInfo.endOffset);
$(targetParagraph).html(beforeText + span.outerHTML + endText);
} else {
const newHtml = textHtml.replace(text, span.outerHTML);
$(targetParagraph).html(newHtml);
}
},
// 找到父级的p
async getParentNode(element, id) {
if (id) {
if (
element.id &&
/^chapter\-item\-\d+$/.test(element.id) &&
Array.from(element.classList).includes("chapter-item")
) {
return element;
}
} else {
const tagArr = ["H1", "H2", "H3", "H4", "H5", "H6", "P"];
if (tagArr.includes(element.nodeName)) {
return element;
}
}
if (element.parentNode) {
return this.getParentNode(element.parentNode, id);
}
return null;
},
// 获取两个标签之间的元素
getParagraphsBetween(startParagraph, endParagraph) {
const paragraphs = [];
let currentParagraph = startParagraph;
while (
currentParagraph &&
currentParagraph !== endParagraph.nextSibling
) {
if (currentParagraph.nodeType === 1) {
paragraphs.push(currentParagraph);
}
currentParagraph = currentParagraph.nextSibling;
}
return paragraphs;
},
// 替换标签
replaceWithElement(lineElementId) {
// 获取包含 line 标签的元素
var lineElement = document.getElementById(lineElementId);
if (lineElement) {
// 创建一个文本节点,包含 line 元素的文本内容
var textNode = document.createTextNode(lineElement.textContent);
// 将文本节点插入到 line 元素之前
lineElement.parentNode.insertBefore(textNode, lineElement);
// 移除原始的 line 元素
lineElement.parentNode.removeChild(lineElement);
}
},
// 计算气泡弹窗位置
calcPosition(ele, modalEle) {
const clickEleSide = $(ele)[0].getBoundingClientRect();
const showEleSide = $(modalEle)[0].getBoundingClientRect();
const showWidth = $(modalEle).innerWidth();
const showHeight = $(modalEle).outerHeight(true);
let squareMiddler = null; // 点击元素正中的位置
let modelLeft = 0;
let modelTop = clickEleSide.top + showHeight + 10;
const lineHeight = this.getLineHeightInPixels($(ele)[0]);
if (clickEleSide.height / 1.5 > lineHeight) {
// 跨行了
squareMiddler = clickEleSide.width - clickEleSide.width / 2;
modelLeft = clickEleSide.right - showWidth;
// 设置尖角的位置
$(modalEle)
.find(".square")
.addClass("square_bottom")
.css({ left: showWidth - 20 });
if (modelTop + showEleSide.height >= this.windowHeight) {
$(modalEle).find('.square').addClass("square_bottom");
modelTop = clickEleSide.top - showEleSide.height - 10;
} else {
$(modalEle).find('.square').addClass("square_top");
modelTop = clickEleSide.top + clickEleSide.height + 10;
}
} else {
squareMiddler = clickEleSide.left + clickEleSide.width / 2;
if (showEleSide.width + clickEleSide.left >= this.clientWidth) {
modelLeft = this.clientWidth - showEleSide.width - 10;
} else {
modelLeft = clickEleSide.left
}
// 设置尖角的位置
$(modalEle)
.find(".square")
.css({ left: squareMiddler - modelLeft - 8 });
if (modelTop + showEleSide.height >= this.windowHeight) {
$(modalEle).find('.square').addClass("square_bottom");
modelTop = clickEleSide.top - showEleSide.height - 10;
} else {
$(modalEle).find('.square').addClass("square_top");
modelTop = clickEleSide.top + clickEleSide.height + 10;
}
}
$(modalEle).css({ left: modelLeft, top: modelTop, opacity: 1 });
},
// 计算操作位置
calcPositionRange(side) {
const selection = window.getSelection();
if (!this.selectedText) return;
var range = selection.getRangeAt(0);
const rangeEle = $(range.startContainer).closest('p,div,h1,h2,h3,h4,h5,h6,li,dd,dt,section');
const rangeLineHeight = $(rangeEle).css("line-height");
const rangeWidth = $(rangeEle).css("width");
const newSide = side || this.side;
const $elem = $(".controll-container");
let top = `${newSide.top - this.rangeOperaTextHeight - 20}px`;
const cpw = $("#chapter-item").find("p").eq(0).innerWidth();
const cHeight = $elem.innerHeight() + 10;
let style1Left = 0; // 操作菜单位置
let squareLeft = 0; // 角的位置
// 角的位置
const center = newSide.width / 2 + newSide.left;
if (this.selectRangeType === "line") {
// 这是单行
if (center > (this.clientWidth * 3) / 4) {
style1Left = this.clientWidth - $elem.outerWidth() - 20;
squareLeft = center - style1Left - 5;
} else if (center < (this.clientWidth * 1) / 4) {
style1Left = newSide.left - 10;
squareLeft = center - style1Left;
} else {
style1Left = (this.clientWidth - $elem.innerWidth()) / 2;
squareLeft = center - style1Left;
}
} else {
// 这是多行
style1Left = newSide.right - $elem.innerWidth();
squareLeft = $elem.innerWidth() - 30;
}
if (style1Left <= 0) {
style1Left = 10;
squareLeft = this.side.right;
}
if (side.top <= cHeight) {
// 上头了
this.rangeSquareType = 'top'
top = newSide.top + newSide.height + 15;
} else {
this.rangeSquareType = 'bottom'
}
$elem.css({ opacity: 1, top: top, left: style1Left });
$(".controll-light-container").css({ top: top, left: style1Left });
$(".controll-container .controll-triangle").css({
left: squareLeft,
}); // 角的位置
},
// 回复初始化
initArags() {
this.nowLineId = -1;
this.nowColorId = -1;
this.boolLightColor = false;
this.boolControllStatus = false;
this.tempCloneRange = null;
this.selectedText = "";
this.rangePosition = {};
this.side = {};
this.containerHasType = false;
this.lineAndLight = false;
this.expandLine = [];
this.expandLight = [];
$(".controll-position .controll-list").css({
transform: `translateY(0px)`,
WebkitTranform: `translateY(0px)`,
});
this.rangeMenuStatus = false;
},
getLineHeightInPixels(element) {
const computedStyle = window.getComputedStyle(element);
const lineHeight = computedStyle.getPropertyValue("line-height");
if (lineHeight === "normal") {
// 创建一个临时元素来计算行高
const tempElement = document.createElement("div");
tempElement.style.fontSize =
computedStyle.getPropertyValue("font-size");
tempElement.innerHTML = "&nbsp;";
element.appendChild(tempElement);
const tempHeight = tempElement.offsetHeight;
element.removeChild(tempElement);
return tempHeight;
} else if (/\dpx$/.test(lineHeight)) {
// 如果 line-height 的值已经是像素值,直接返回
return parseInt(lineHeight, 10);
} else if (/\d+$/.test(lineHeight)) {
// 如果 line-height 的值是纯数字,表示倍数,需要计算实际像素值
return (
parseInt(computedStyle.getPropertyValue("font-size"), 10) *
parseFloat(lineHeight)
);
} else if (/\d+%$/.test(lineHeight)) {
// 如果 line-height 的值是百分比,需要计算实际像素值
return (
parseInt(computedStyle.getPropertyValue("font-size"), 10) *
(parseFloat(lineHeight) / 100)
);
} else {
// 其他情况,暂时返回 0
return 0;
}
},
// 接收到了高亮或划线的id
toNoteIdSide() {
if (this.noteId > 0) {
$("line, light").each((index, item) => {
const id = $(item).data("id");
if (parseInt(id) === parseInt(this.noteId)) {
const idSide = $(item)[0].getBoundingClientRect();
const sideTop = idSide.top - 50;
$("#chapter-content-list .show").css({
transform: `translateY(-${sideTop}px)`,
WebkitTranform: `translateY(-${sideTop}px)`,
});
}
});
this.noteId = -1;
}
$("video, audio").each((index, item) => {
$(item).attr("controlslist", "nodownload");
});
},
// flutter 回调
async callbackInFlutterComponent(args) {
try {
const data = args.split(",");
this.baseURL = data[0];
this.book_id = data[1];
this.chapter_id = data[2];
this.token = data[3];
this.noteId = data[4];
this.searchText = data[5];
localStorage.setItem("token", data[3]);
setTimeout(async () => {
await this.getBookChapterData();
}, 200);
} catch (e) {
console.log(e);
}
},
async refreshTokenSuccess(args) {
try {
this.token = args.token;
localStorage.setItem("token", args.token);
this.getBookChapterData();
} catch (e) {
console.log(e);
}
},
// 通知flutter组件
callHandlerNoticeFlutterSubmit(funcname = "evaluatingSubmit", data) {
if (
window.flutter_inappwebview &&
window.flutter_inappwebview.callHandler
) {
window.flutter_inappwebview
.callHandler(funcname, data)
.then(function (result) {
console.log("Result from Flutter:", result);
});
}
},
},
});
const callbackInFlutterComponent = (arguments) => {
return app.callbackInFlutterComponent(arguments);
};
const refreshTokenSuccess = (arguments) => {
return app.refreshTokenSuccess(arguments);
};
// callbackInFlutterComponent(',175,335,2f85d063f94485c0c2ef31e652875bd5');
</script>
</body>
</html>
...@@ -6,8 +6,9 @@ ...@@ -6,8 +6,9 @@
name="viewport" name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=0" content="width=device-width, initial-scale=1.0, user-scalable=0"
/> />
<title>测评-紫荆教育云教材</title> <title>测评-紫荆教育云教材</title>
<link rel="stylesheet" href="assets/css/evaluating.css" /> <script src="assets/js/config.js"></script>
<link rel="stylesheet" href="../assets/css/evaluating.css" />
<script src="assets/js/flexible.min.js"></script> <script src="assets/js/flexible.min.js"></script>
<script src="assets/js/jquery.2.2.4.min.js"></script> <script src="assets/js/jquery.2.2.4.min.js"></script>
<script src="assets/js/qs.6.11.2.min.js"></script> <script src="assets/js/qs.6.11.2.min.js"></script>
...@@ -327,7 +328,7 @@ ...@@ -327,7 +328,7 @@
showHeight: showHeight:
document.documentElement.clientHeight || document.body.clientHeight, document.documentElement.clientHeight || document.body.clientHeight,
hideshow: true, //显示或者隐藏footer hideshow: true, //显示或者隐藏footer
baseURL: "", baseURL: window.BOOK_API_BASE_URL,
book_id: "", // 175 book_id: "", // 175
chapter_id: "", // 335 chapter_id: "", // 335
token: "", token: "",
...@@ -569,13 +570,23 @@ ...@@ -569,13 +570,23 @@
position: this.position, position: this.position,
}); });
if (res.code === 200) { if (res.code === 200) {
this.callHandlerNoticeFlutterSubmit("dismissLoadingCallback"); this.callHandlerNoticeFlutterSubmit("dismissLoadingCallback");
if (res.data.is_success === 1) { if (res.data.is_success === 1) {
this.callHandlerNoticeFlutterSubmit( const resultUrl = new URL(
"submitSuccessCallback" "evaluating_result.html",
); window.location.href
} );
resultUrl.searchParams.set("book_id", this.book_id);
resultUrl.searchParams.set("chapter_id", this.chapter_id);
resultUrl.searchParams.set("position", this.position);
resultUrl.searchParams.set("source", "app");
resultUrl.searchParams.set("title", document.title);
window.flutter_inappwebview?.callHandler(
"openWebview",
resultUrl.toString()
);
}
} else { } else {
this.callHandlerNoticeFlutterSubmit("dismissLoadingCallback"); this.callHandlerNoticeFlutterSubmit("dismissLoadingCallback");
} }
...@@ -607,19 +618,28 @@ ...@@ -607,19 +618,28 @@
})(); })();
}; };
}, },
// flutter 回调 // flutter 回调
async callbackInFlutterComponent(args) { async callbackInFlutterComponent(args) {
try { try {
const data = args.split(","); if (args && typeof args === "object") {
this.baseURL = data[0]; this.baseURL = window.BOOK_API_BASE_URL;
this.book_id = data[1]; this.book_id = args.book_id || "";
this.chapter_id = data[2]; this.chapter_id = args.chapter_id || "";
this.token = data[3]; this.token = args.token || localStorage.getItem("token") || "";
this.position = data[4]; this.position = args.position || "";
localStorage.setItem("token", data[3]); } else {
setTimeout(async () => { const data = args.split(",");
await this.getEvaluatingData(); const offset = data.length >= 5 ? 1 : 0;
}, 200); this.baseURL = window.BOOK_API_BASE_URL;
this.book_id = data[offset];
this.chapter_id = data[offset + 1];
this.token = data[offset + 2];
this.position = data[offset + 3];
}
if (this.token) localStorage.setItem("token", this.token);
setTimeout(async () => {
await this.getEvaluatingData();
}, 200);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
...@@ -731,10 +751,20 @@ ...@@ -731,10 +751,20 @@
const callbackInFlutterComponent = (arguments) => { const callbackInFlutterComponent = (arguments) => {
return app.callbackInFlutterComponent(arguments); return app.callbackInFlutterComponent(arguments);
}; };
const refreshTokenSuccess = (arguments) => { const refreshTokenSuccess = (arguments) => {
return app.refreshTokenSuccess(arguments); return app.refreshTokenSuccess(arguments);
}; };
// callbackInFlutterComponent(',175,2834,726cca8efcb328672202656f63e176e8,bgi0jn6p'); const urlParams = new URLSearchParams(window.location.search);
const pageTitle = urlParams.get('title');
if (pageTitle) document.title = pageTitle;
if (urlParams.get('book_id') && urlParams.get('chapter_id')) {
app.callbackInFlutterComponent({
book_id: urlParams.get('book_id'),
chapter_id: urlParams.get('chapter_id'),
position: urlParams.get('position') || '',
});
}
// callbackInFlutterComponent(',175,2834,726cca8efcb328672202656f63e176e8,bgi0jn6p');
</script> </script>
</body> </body>
</html> </html>
...@@ -6,9 +6,10 @@ ...@@ -6,9 +6,10 @@
name="viewport" name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=0" content="width=device-width, initial-scale=1.0, user-scalable=0"
/> />
<title>测评-紫荆教育云教材</title> <title>测评-紫荆教育云教材</title>
<link rel="stylesheet" href="assets/css/common.css" /> <script src="assets/js/config.js"></script>
<link rel="stylesheet" href="assets/css/evaluating.css" /> <link rel="stylesheet" href="../assets/css/common.css" />
<link rel="stylesheet" href="../assets/css/evaluating.css" />
<script src="assets/js/flexible.min.js"></script> <script src="assets/js/flexible.min.js"></script>
<script src="assets/js/jquery.2.2.4.min.js"></script> <script src="assets/js/jquery.2.2.4.min.js"></script>
<script src="assets/js/qs.6.11.2.min.js"></script> <script src="assets/js/qs.6.11.2.min.js"></script>
...@@ -341,7 +342,7 @@ ...@@ -341,7 +342,7 @@
showHeight: showHeight:
document.documentElement.clientHeight || document.body.clientHeight, document.documentElement.clientHeight || document.body.clientHeight,
hideshow: true, //显示或者隐藏footer hideshow: true, //显示或者隐藏footer
baseURL: "", baseURL: window.BOOK_API_BASE_URL,
book_id: "", // 323 book_id: "", // 323
chapter_id: "", // 2635 chapter_id: "", // 2635
token: "", token: "",
...@@ -514,19 +515,28 @@ ...@@ -514,19 +515,28 @@
this.$forceUpdate(); this.$forceUpdate();
}); });
}, },
// flutter 回调 // flutter 回调
async callbackInFlutterComponent(args) { async callbackInFlutterComponent(args) {
try { try {
const data = args.split(","); if (args && typeof args === "object") {
this.baseURL = data[0]; this.baseURL = window.BOOK_API_BASE_URL;
this.book_id = data[1]; this.book_id = args.book_id || "";
this.chapter_id = data[2]; this.chapter_id = args.chapter_id || "";
this.token = data[3]; this.token = args.token || localStorage.getItem("token") || "";
this.position = data[4]; this.position = args.position || "";
localStorage.setItem("token", data[3]); } else {
setTimeout(async () => { const data = args.split(",");
await this.getEvaluatingData(); const offset = data.length >= 5 ? 1 : 0;
}, 200); this.baseURL = window.BOOK_API_BASE_URL;
this.book_id = data[offset];
this.chapter_id = data[offset + 1];
this.token = data[offset + 2];
this.position = data[offset + 3];
}
if (this.token) localStorage.setItem("token", this.token);
setTimeout(async () => {
await this.getEvaluatingData();
}, 200);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
...@@ -635,10 +645,20 @@ ...@@ -635,10 +645,20 @@
}, },
}); });
const callbackInFlutterComponent = (arguments) => { const callbackInFlutterComponent = (arguments) => {
return app.callbackInFlutterComponent(arguments); return app.callbackInFlutterComponent(arguments);
}; };
// callbackInFlutterComponent(',175,2834,726cca8efcb328672202656f63e176e8,bgi0jn6p'); const urlParams = new URLSearchParams(window.location.search);
const pageTitle = urlParams.get('title');
if (pageTitle) document.title = pageTitle;
if (urlParams.get('book_id') && urlParams.get('chapter_id')) {
app.callbackInFlutterComponent({
book_id: urlParams.get('book_id'),
chapter_id: urlParams.get('chapter_id'),
position: urlParams.get('position') || '',
});
}
// callbackInFlutterComponent(',175,2834,726cca8efcb328672202656f63e176e8,bgi0jn6p');
</script> </script>
</body> </body>
</html> </html>
...@@ -6,9 +6,10 @@ ...@@ -6,9 +6,10 @@
name="viewport" name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=0" content="width=device-width, initial-scale=1.0, user-scalable=0"
/> />
<title>测评-紫荆教育云教材</title> <title>错题详情</title>
<link rel="stylesheet" href="assets/css/common.css" /> <script src="assets/js/config.js"></script>
<link rel="stylesheet" href="assets/css/evaluating.css" /> <link rel="stylesheet" href="../assets/css/common.css" />
<link rel="stylesheet" href="../assets/css/evaluating.css" />
<script src="assets/js/flexible.min.js"></script> <script src="assets/js/flexible.min.js"></script>
<script src="assets/js/jquery.2.2.4.min.js"></script> <script src="assets/js/jquery.2.2.4.min.js"></script>
<script src="assets/js/qs.6.11.2.min.js"></script> <script src="assets/js/qs.6.11.2.min.js"></script>
...@@ -29,7 +30,7 @@ ...@@ -29,7 +30,7 @@
:src="book_info.img" :src="book_info.img"
alt="book logo" alt="book logo"
/> />
<img v-else src="./assets/images/wrong.png" alt="book logo" /> <img v-else src="../assets/images/wrong.png" alt="book logo" />
</div> </div>
</div> </div>
<div class="book-title-info"> <div class="book-title-info">
...@@ -192,7 +193,7 @@ ...@@ -192,7 +193,7 @@
showHeight: showHeight:
document.documentElement.clientHeight || document.body.clientHeight, document.documentElement.clientHeight || document.body.clientHeight,
hideshow: true, //显示或者隐藏footer hideshow: true, //显示或者隐藏footer
baseURL: "", baseURL: window.BOOK_API_BASE_URL,
book_id: '', book_id: '',
token: "", token: "",
book_info: {}, book_info: {},
...@@ -312,16 +313,20 @@ ...@@ -312,16 +313,20 @@
this.$forceUpdate(); this.$forceUpdate();
}); });
}, },
// flutter 回调 // flutter 回调
async callbackInFlutterComponent(args) { async callbackInFlutterComponent(args) {
try { try {
const data = args.split(','); const data = args && typeof args === "object" ? args : null;
this.baseURL = data[0] const values = data ? [] : args.split(',');
this.book_id = data[1]; const offset = values.length >= 3 ? 1 : 0;
this.token = data[2]; this.baseURL = window.BOOK_API_BASE_URL;
localStorage.setItem("token", data[2]); this.book_id = data ? data.book_id : values[offset];
setTimeout(async () => { this.token = data
await this.getEvaluatingData(); ? data.token || localStorage.getItem("token") || ""
: values[offset + 1];
if (this.token) localStorage.setItem("token", this.token);
setTimeout(async () => {
await this.getEvaluatingData();
}, 200) }, 200)
} catch (e) { } catch (e) {
console.log(e); console.log(e);
...@@ -361,9 +366,17 @@ ...@@ -361,9 +366,17 @@
}, },
}); });
const callbackInFlutterComponent = (arguments) => { const callbackInFlutterComponent = (arguments) => {
return app.callbackInFlutterComponent(arguments); return app.callbackInFlutterComponent(arguments);
} }
</script>
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get('book_id')) {
app.callbackInFlutterComponent({
book_id: urlParams.get('book_id'),
token: localStorage.getItem('token') || '',
});
}
</script>
</body> </body>
</html> </html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=0"
/>
<title>阅读详情-紫荆教育云教材</title>
<script src="assets/js/config.js"></script>
<link rel="stylesheet" href="../assets/css/common.css" />
<link rel="stylesheet" href="../assets/css/read.css" />
<link rel="stylesheet" href="../assets/css/github.css" />
<script src="assets/js/flexible.min.js"></script>
<script src="assets/js/highlight.min.js"></script>
<script src="assets/js/vue@2.js"></script>
<script src="assets/js/jquery.2.2.4.min.js"></script>
<script src="assets/js/qs.6.11.2.min.js"></script>
<script src="assets/js/blueimp-md5.2.19.0.min.js"></script>
<script src="assets/js/axios.min.js"></script>
<script src="assets/js/request.js"></script>
<script src="assets/js/iconfont.js"></script>
<!-- <script src="assets/js/vconsole.min.js"></script> -->
</head>
<body class="body_evaluating">
<div id="app" v-cloak>
<div class="reader-loading" v-show="nextChapterLoading">加载中...</div>
<div class="chapter-container" ref="chapterContainer">
<div class="chapter-content-list" id="chapter-content-list">
<div class="show-up">
<div
class="chapter-item-up"
:class="[`chapter-item-up-${prev_chapter.id}`]"
id="chapter-item-up"
:data-id="prev_chapter.id"
v-html="prev_chapter.contents"
></div>
</div>
<div class="show">
<div
class="chapter-item"
:class="[`chapter-item-${chapter_id}`]"
id="chapter-item"
:data-id="chapter_id"
v-html="chapterContent"
v-show="!lockedContent"
></div>
<div class="lock-content" v-show="lockedContent">
<div class="lock-img">
<div class="img"></div>
</div>
<div class="lock-desc">未解锁章节</div>
<div class="lock-button">
<span>获取完整版</span>
</div>
</div>
</div>
</div>
<div class="range-controll">
<div
class="controll-container"
v-show="boolControllStatus"
:class="[nowColorId > 0 ? 'colorLine' : '']"
>
<!-- <div class="controll-container" :class="[nowColorId > 0 ? 'colorLine' : '']"> -->
<div class="controll-position">
<div
v-show="rangeMenuStatus"
class="com prev"
@click.stop.prevent="rangeMenuMove('prev', $event)"
><i class="ico"></i></div>
<div class="controll-list-container">
<div class="controll-list">
<div class="controll-item colorLine" v-if="nowColorId > 0">
<span
v-for="(item, index) in colorList"
:class="[`c-${item}`]"
:key="index"
@click.stop.prevent="rangeTextChangeLight"
></span>
</div>
<div
class="controll-item"
@click.stop.prevent="rangeTextInFlutter('discuss', $event)"
>
<div class="opa discuss"><i class="ico"></i></div>
<p>讨论</p>
</div>
<div
class="controll-item"
@click.stop.prevent="rangeTextInFlutter('note', $event)"
>
<div class="opa note"><i class="ico"></i></div>
<p>笔记</p>
</div>
<div
class="controll-item"
@click.stop.prevent="rangeTextInLine($event)"
>
<div class="opa line"><i class="ico"></i></div>
<p v-if="nowLineId > 0">移除划线</p>
<p v-else-if="expandLine.length > 0">扩展划线</p>
<p class="cancel" v-else>划线</p>
</div>
<div
class="controll-item light-color"
@click.stop.prevent="rangeTextInLight($event)"
>
<div class="opa light"><i class="ico"></i></div>
<p v-if="nowColorId > 0">移除高亮</p>
<p v-else-if="expandLight.length > 0">扩展高亮</p>
<p class="cancel" v-else>高亮</p>
</div>
<div
class="controll-item"
@click.stop.prevent="rangeTextInFlutter('baike', $event)"
>
<div class="opa baike"><i class="ico"></i></div>
<p>百科</p>
</div>
<div
class="controll-item"
@click.stop.prevent="rangeTextInFlutter('dict', $event)"
>
<div class="opa dict"><i class="ico"></i></div>
<p>字典</p>
</div>
</div>
</div>
<div
v-show="!rangeMenuStatus"
class="com next"
@click.stop.prevent="rangeMenuMove('next', $event)"
><i class="ico"></i></div>
</div>
<div class="controll-triangle top" v-show="rangeSquareType === 'top'"></div>
<div class="controll-triangle bottom" v-show="rangeSquareType === 'bottom'"></div>
</div>
<!-- light -->
<div class="controll-light-container" v-show="boolLightColor">
<div class="controll-color-list">
<div
class="controll-color-item"
:class="[`c-${item}`, nowActiveColor === item ? 'active' : '']"
v-for="(item, index) in colorList"
:key="index"
@click="setLightColorText(item)"
>
<div class="color"></div>
</div>
</div>
</div>
</div>
<div class="tange-tooptip" v-show="tooltipId">
<div class="square" :style=""></div>
<div class="tooltip-content-container">
<div class="tooltip-content">
<div class="content">
<p>{{tooltipContent.content}}</p>
</div>
<div class="content-opa" v-if="tooltipContent.link">
<a
:href="tooltipContent.link"
target="_blank"
rel="noreferrer"
class="c-link"
>
<svg class="svg-icon" style="color: #ab1941; fill: currentColor; overflow: hidden; width: 20px; height: 20px;" aria-hidden="true"><use xlink:href="#icon-a-link"></use></svg>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="module" src="reader/read-app.js"></script>
</body>
</html>
...@@ -3,9 +3,10 @@ ...@@ -3,9 +3,10 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<title>阅读详情-紫荆教育云教材</title> <title>阅读详情-紫荆教育云教材</title>
<link rel="stylesheet" href="assets/css/common.css" /> <script src="assets/js/config.js"></script>
<link rel="stylesheet" href="assets/css/read.css" /> <link rel="stylesheet" href="../assets/css/common.css" />
<link rel="stylesheet" href="../assets/css/read.css" />
<script src="assets/js/flexible.min.js"></script> <script src="assets/js/flexible.min.js"></script>
<script src="assets/js/vue@2.js"></script> <script src="assets/js/vue@2.js"></script>
<script src="assets/js/jquery.2.2.4.min.js"></script> <script src="assets/js/jquery.2.2.4.min.js"></script>
...@@ -64,10 +65,18 @@ ...@@ -64,10 +65,18 @@
}, },
}); });
const callbackInFlutterComponent = (arguments) => { const callbackInFlutterComponent = (arguments) => {
return app.callbackInFlutterComponent(arguments); return app.callbackInFlutterComponent(arguments);
}; };
// callbackInFlutterComponent(); const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('title')) document.title = urlParams.get('title') || '';
if (urlParams.get('url')) {
app.callbackInFlutterComponent({
url: urlParams.get('url'),
title: urlParams.get('title') || '',
});
}
// callbackInFlutterComponent();
</script> </script>
</body> </body>
</html> </html>
...@@ -6,10 +6,11 @@ ...@@ -6,10 +6,11 @@
name="viewport" name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=0" content="width=device-width, initial-scale=1.0, user-scalable=0"
/> />
<title>阅读详情-紫荆教育云教材</title> <title>阅读详情-紫荆教育云教材</title>
<link rel="stylesheet" href="assets/css/common.css" /> <script src="assets/js/config.js"></script>
<link rel="stylesheet" href="assets/css/read.css" /> <link rel="stylesheet" href="../assets/css/common.css" />
<link rel="stylesheet" href="assets/css/github.css" /> <link rel="stylesheet" href="../assets/css/read.css" />
<link rel="stylesheet" href="../assets/css/github.css" />
<script src="assets/js/flexible.min.js"></script> <script src="assets/js/flexible.min.js"></script>
<script src="assets/js/highlight.min.js"></script> <script src="assets/js/highlight.min.js"></script>
<script src="assets/js/vue@2.js"></script> <script src="assets/js/vue@2.js"></script>
...@@ -45,14 +46,26 @@ ...@@ -45,14 +46,26 @@
</div> </div>
</div> </div>
<script> <script>
function openWebview(page, params = {}) {
const url = new URL(page, window.location.href);
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
url.searchParams.set(key, value);
}
});
if (window.flutter_inappwebview && window.flutter_inappwebview.callHandler) {
return window.flutter_inappwebview.callHandler("openWebview", url.toString());
}
}
let getChapterGalleryURL = "/v1/book/Information/getGalleryByPosition"; // 文章内容中画廊内容 let getChapterGalleryURL = "/v1/book/Information/getGalleryByPosition"; // 文章内容中画廊内容
let getChapterExpandContentURL = "/v1/book/Information/getExtendByPosition"; // 获取扩展的内容 let getChapterExpandContentURL = "/v1/book/Information/getExtendByPosition"; // 获取扩展的内容
var app = new Vue({ var app = new Vue({
el: "#app", el: "#app",
data: { data: {
baseURL: "", baseURL: window.BOOK_API_BASE_URL,
book_id: "", // 175 358 book_id: "", // 175 358
chapter_id: "", // 2253 2825 chapter_id: "", // 2253 2825
token: "", // token token: "", // token
...@@ -166,12 +179,12 @@ ...@@ -166,12 +179,12 @@
if (target.nodeName.toLowerCase() === "img") { if (target.nodeName.toLowerCase() === "img") {
if ($(target).closest(".show-expand").length > 0) { if ($(target).closest(".show-expand").length > 0) {
const scaleImg = $(target).attr("src"); const scaleImg = $(target).attr("src");
this.callHandlerNoticeFlutterSubmit("scaleImageCallback", [scaleImg, ""]); openWebview("read_img.html", { url: scaleImg });
ev.stopPropagation(); ev.stopPropagation();
ev.preventDefault(); ev.preventDefault();
} else if ($(target).closest(".gallery-pic").length > 0) { } else if ($(target).closest(".gallery-pic").length > 0) {
const scaleImg = $(target).attr("src"); const scaleImg = $(target).attr("src");
this.callHandlerNoticeFlutterSubmit("scaleImageCallback", [scaleImg, ""]); openWebview("read_img.html", { url: scaleImg });
ev.stopPropagation(); ev.stopPropagation();
ev.preventDefault(); ev.preventDefault();
} }
...@@ -208,13 +221,15 @@ ...@@ -208,13 +221,15 @@
} }
}, },
// flutter 回调 // flutter 回调
async callbackInFlutterComponent(args) { async callbackInFlutterComponent(args) {
this.chapter_id = args.chapter_id; this.chapter_id = args.chapter_id;
this.book_id = args.book_id; this.book_id = args.book_id;
localStorage.setItem("token", args.token); if (args.token) {
this.baseURL = args.base_url; localStorage.setItem("token", args.token);
this.type = args.type; }
this.position = args.position; this.baseURL = window.BOOK_API_BASE_URL;
this.type = args.type;
this.position = args.position;
this.getData(); this.getData();
}, },
// 通知flutter组件 // 通知flutter组件
...@@ -228,10 +243,21 @@ ...@@ -228,10 +243,21 @@
}, },
}); });
const callbackInFlutterComponent = (arguments) => { const callbackInFlutterComponent = (arguments) => {
return app.callbackInFlutterComponent(arguments); return app.callbackInFlutterComponent(arguments);
}; };
// callbackInFlutterComponent({ base_url: '', book_id: 175, chapter_id: 2834, token: '726cca8efcb328672202656f63e176e8', position: '49732578', type: 'gallery'}); const urlParams = new URLSearchParams(window.location.search);
const pageTitle = urlParams.get('title');
if (pageTitle) document.title = pageTitle;
if (urlParams.get('book_id') && urlParams.get('chapter_id')) {
app.callbackInFlutterComponent({
book_id: urlParams.get('book_id'),
chapter_id: urlParams.get('chapter_id'),
position: urlParams.get('position') || '',
type: urlParams.get('type') || '',
});
}
// callbackInFlutterComponent({ book_id: 175, chapter_id: 2834, token: 'token', position: '49732578', type: 'gallery'});
</script> </script>
</body> </body>
</html> </html>
# Reader runtime
`read.html` is the single reader entry for online and offline chapter data.
- `reader-config.js`: reader API endpoint definitions.
- `reader-api.js`: API adapter shared by the reader runtime.
- `reader-bridge.js`: Flutter bridge adapter; browser usage is a no-op.
- `reader-content.js`: offline payload normalization and mode detection.
- `read-app.js`: Vue reader runtime and interaction behavior.
The App can continue calling `callbackInFlutterComponent()` with the existing
comma-separated online payload. Offline mode passes the chapter payload object
to the same callback. The reader then renders both modes through
`renderChapterData()`.
For direct browser access, pass `book_id`, `chapter_id`, and optionally
`token`, `note_id`, and `search_text` in the URL query string. The API base
URL is defined by the H5 runtime configuration.
The page then requests the chapter content itself.
<!DOCTYPE html> import { DEFAULT_API_BASE_URL, READER_ENDPOINTS } from "./reader-config.js";
<html lang="en"> import { readerApi } from "./reader-api.js";
<head> import { readerBridge } from "./reader-bridge.js";
<meta charset="UTF-8" /> import { isOfflinePayload, normalizeOfflinePayload } from "./reader-content.js";
<meta
name="viewport" const Vue = window.Vue;
content="width=device-width, initial-scale=1.0, user-scalable=0" const $ = window.jQuery || window.$;
/> const hljs = window.hljs;
<title>阅读详情-紫荆教育云教材</title> const request = readerApi;
<link rel="stylesheet" href="assets/css/common.css" /> const {
<link rel="stylesheet" href="assets/css/read.css" /> chapterContent: getChapterContentURL,
<link rel="stylesheet" href="assets/css/github.css" /> chapterProgress: getChapterContentProgressURL,
<script src="assets/js/flexible.min.js"></script> chapterRecord: getChapterContentRecordURL,
<script src="assets/js/highlight.min.js"></script> addNotes: addInfomationNotes,
<script src="assets/js/vue@2.js"></script> editNotes: editInfomationNotes,
<script src="assets/js/jquery.2.2.4.min.js"></script> deleteNotes: delInfomationNotes,
<script src="assets/js/qs.6.11.2.min.js"></script> deleteNotesBatch: delBatchInfomationNotes,
<script src="assets/js/blueimp-md5.2.19.0.min.js"></script> checkPractice: checkPracticeInPosition,
<script src="assets/js/axios.min.js"></script> checkTooltip: checkTooptipInPosition,
<script src="assets/js/request.js"></script> } = READER_ENDPOINTS;
<script src="assets/js/iconfont.js"></script>
<script>
function countSubstringOccurrences(str, substring) { function countSubstringOccurrences(str, substring) {
// 如果子串没有出现,match方法将返回null // 如果子串没有出现,match方法将返回null
...@@ -33,176 +31,8 @@ function countSubstringOccurrences(str, substring) { ...@@ -33,176 +31,8 @@ function countSubstringOccurrences(str, substring) {
// 返回匹配项的数量,即子串出现的次数 // 返回匹配项的数量,即子串出现的次数
return matches.length; return matches.length;
} }
</script>
<!-- <script src="assets/js/vconsole.min.js"></script> --> // var vConsole = new VConsole();
</head>
<body class="body_evaluating">
<div id="app" v-cloak>
<div class="chapter-container" ref="chapterContainer">
<div
class="load-chapter prev-chapter"
v-show="prev_chapter && scrollLoading"
>
<div class="chapter-c-title">
上一节: <strong>{{ prev_chapter.name }}</strong>
</div>
</div>
<div
class="load-chapter next-chapter"
v-show="next_chapter && scrollLoading"
>
<div class="chapter-c-title">下一节: {{ next_chapter.name }}</div>
</div>
<div class="chapter-content-list" id="chapter-content-list">
<div class="show-up">
<div
class="chapter-item-up"
:class="[`chapter-item-up-${prev_chapter.id}`]"
id="chapter-item-up"
:data-id="prev_chapter.id"
v-html="prev_chapter.contents"
></div>
</div>
<div class="show">
<div
class="chapter-item"
:class="[`chapter-item-${chapter_id}`]"
id="chapter-item"
:data-id="chapter_id"
v-html="chapterContent"
></div>
</div>
</div>
<div class="range-controll">
<div
class="controll-container"
v-show="boolControllStatus"
:class="[nowColorId > 0 ? 'colorLine' : '']"
>
<!-- <div class="controll-container" :class="[nowColorId > 0 ? 'colorLine' : '']"> -->
<div class="controll-position">
<div
v-show="rangeMenuStatus"
class="com prev"
@click.stop.prevent="rangeMenuMove('prev', $event)"
><i class="ico"></i></div>
<div class="controll-list-container">
<div class="controll-list">
<div class="controll-item colorLine" v-if="nowColorId > 0">
<span
v-for="(item, index) in colorList"
:class="[`c-${item}`]"
:key="index"
@click.stop.prevent="rangeTextChangeLight"
></span>
</div>
<div
class="controll-item"
@click.stop.prevent="rangeTextInFlutter('discuss', $event)"
>
<div class="opa discuss"><i class="ico"></i></div>
<p>讨论</p>
</div>
<div
class="controll-item"
@click.stop.prevent="rangeTextInFlutter('note', $event)"
>
<div class="opa note"><i class="ico"></i></div>
<p>笔记</p>
</div>
<div
class="controll-item"
@click.stop.prevent="rangeTextInLine($event)"
>
<div class="opa line"><i class="ico"></i></div>
<p v-if="nowLineId > 0">移除划线</p>
<p v-else-if="expandLine.length > 0">扩展划线</p>
<p class="cancel" v-else>划线</p>
</div>
<div
class="controll-item light-color"
@click.stop.prevent="rangeTextInLight($event)"
>
<div class="opa light"><i class="ico"></i></div>
<p v-if="nowColorId > 0">移除高亮</p>
<p v-else-if="expandLight.length > 0">扩展高亮</p>
<p class="cancel" v-else>高亮</p>
</div>
<div
class="controll-item"
@click.stop.prevent="rangeTextInFlutter('baike', $event)"
>
<div class="opa baike"><i class="ico"></i></div>
<p>百科</p>
</div>
<div
class="controll-item"
@click.stop.prevent="rangeTextInFlutter('dict', $event)"
>
<div class="opa dict"><i class="ico"></i></div>
<p>字典</p>
</div>
<div
class="controll-item"
@click.stop.prevent="rangeTextInFlutter('read', $event)"
>
<div class="opa read"><i class="ico"></i></div>
<p>朗读</p>
</div>
</div>
</div>
<div
v-show="!rangeMenuStatus"
class="com next"
@click.stop.prevent="rangeMenuMove('next', $event)"
><i class="ico"></i></div>
</div>
<div class="controll-triangle top" v-show="rangeSquareType === 'top'"></div>
<div class="controll-triangle bottom" v-show="rangeSquareType === 'bottom'"></div>
</div>
<!-- light -->
<div class="controll-light-container" v-show="boolLightColor">
<div class="controll-color-list">
<div
class="controll-color-item"
:class="[`c-${item}`, nowActiveColor === item ? 'active' : '']"
v-for="(item, index) in colorList"
:key="index"
@click="setLightColorText(item)"
>
<div class="color"></div>
</div>
</div>
</div>
</div>
<div class="tange-tooptip" v-show="tooltipId">
<div class="square" :style=""></div>
<div class="tooltip-content-container">
<div class="tooltip-content">
<div class="content">
<p>{{tooltipContent.content}}</p>
</div>
<div class="content-opa" v-if="tooltipContent.link">
<a
:href="tooltipContent.link"
target="_blank"
rel="noreferrer"
class="c-link"
>
<svg class="svg-icon" style="color: #ab1941; fill: currentColor; overflow: hidden; width: 20px; height: 20px;" aria-hidden="true"><use xlink:href="#icon-a-link"></use></svg>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
let checkPracticeInPosition = "/v1/book/Information/checkChapterQuestion"; // 是否答题检测
let checkTooptipInPosition = "/v1/book/Information/getBubbleByPosition"; // 获取气泡内容
var app = new Vue({ var app = new Vue({
el: "#app", el: "#app",
...@@ -219,9 +49,11 @@ function countSubstringOccurrences(str, substring) { ...@@ -219,9 +49,11 @@ function countSubstringOccurrences(str, substring) {
"d2b3fa", "d2b3fa",
"ff4500", "ff4500",
], ],
hideshow: true, //显示或者隐藏footer hideshow: true, //显示或者隐藏footer
baseURL: "", baseURL: DEFAULT_API_BASE_URL,
book_id: "", // 358 contentMode: "online",
offlinePayload: null,
book_id: "", // 358
chapter_id: "", // 1931 343 2821 chapter_id: "", // 1931 343 2821
token: "", // token token: "", // token
searchText: "", searchText: "",
...@@ -234,7 +66,6 @@ function countSubstringOccurrences(str, substring) { ...@@ -234,7 +66,6 @@ function countSubstringOccurrences(str, substring) {
prev_chapter: "", // 上一个章节的id prev_chapter: "", // 上一个章节的id
rangeSquareType: 'top', rangeSquareType: 'top',
nextChapterLoading: false, nextChapterLoading: false,
scrollTop: 0,
readTimer: null, readTimer: null,
debounceTimeout: false, // 防抖设置 debounceTimeout: false, // 防抖设置
selectedText: "", // 选择的文本 selectedText: "", // 选择的文本
...@@ -246,7 +77,6 @@ function countSubstringOccurrences(str, substring) { ...@@ -246,7 +77,6 @@ function countSubstringOccurrences(str, substring) {
nowLineId: -1, nowLineId: -1,
color_line: [], color_line: [],
nowColorId: -1, nowColorId: -1,
touchIn: false, // 按下
touchstartTime: null, // 按下的时间 touchstartTime: null, // 按下的时间
touchLastClickTime: null, // 按下时间-双击检测 touchLastClickTime: null, // 按下时间-双击检测
nowActiveColor: "", nowActiveColor: "",
...@@ -275,104 +105,113 @@ function countSubstringOccurrences(str, substring) { ...@@ -275,104 +105,113 @@ function countSubstringOccurrences(str, substring) {
], ],
isMobile: "ontouchstart" in window || navigator.maxTouchPoints, isMobile: "ontouchstart" in window || navigator.maxTouchPoints,
containerHasType: false, containerHasType: false,
translateY: 0, // 默认的初始位置
scrollPageY: 0, // 鼠标或手指按下的位置 scrollPageY: 0, // 鼠标或手指按下的位置
scrollLoading: false, // 鼠标或手指按下的位置 touchStartedAtTop: false,
touchStartedAtBottom: false,
tooltipStatus: null, // 气泡展示状态 tooltipStatus: null, // 气泡展示状态
tooltipId: null, // 气泡id tooltipId: null, // 气泡id
tooltipContent: {}, // 气泡内容 tooltipContent: {}, // 气泡内容
noteId: -1, // noteId: -1, //
velocity: 0.6, // 初始速度 notesType: "",
friction: 0.03, // 摩擦系数 eventsBound: false, // 触摸事件只绑定一次
animationId: null, // 动画id lockedContent: false, // 章节被锁
animationType: null, // 上滑还是下滑
notesType: '', // 笔记类型 - 划线 line 高连 light
}, },
created() {}, created() {},
async mounted() { async mounted() {
this.windowHeight = document.documentElement.clientHeight; this.windowHeight = document.documentElement.clientHeight;
this.chapterContent = null; this.chapterContent = null;
this.rangeOperaTextHeight = $(".controll-container").outerHeight(
this.rangeOperaTextHeight = $(".controll-container").outerHeight(true); true
this.rangeOperaColorHeight = $(".controll-light-container").outerHeight(true); );
this.rangeOperaColorHeight = $(
".controll-light-container"
).outerHeight(true);
// selection range 设置 // selection range 设置
this.selectionRangeOperation(); this.selectionRangeOperation();
this.callHandlerNoticeFlutterSubmit("offlineDomLoadSuccessCallBack"); },
}, unmounted() {
unmounted() { clearInterval(this.readTimer);
clearInterval(this.readTimer); },
},
watch: { watch: {
side(newVal, oldVal) { side(newVal, oldVal) {
if (newVal) { if (newVal) {
this.calcPositionRange(newVal); this.calcPositionRange(newVal);
} }
}, },
}, },
methods: { methods: {
async getBookChapterData(args, scroll) { async renderChapterData(data, scroll, offline = false) {
if (this.nextChapterLoading) return; this.lockedContent = false;
// loading this.next_chapter = data.next_chapter || {};
this.callHandlerNoticeFlutterSubmit("showLoadingCallback"); this.prev_chapter = data.up_chapter || {};
this.color_line = Array.isArray(data.color_line) ? data.color_line : [];
this.line_list = Array.isArray(data.line_list) ? data.line_list : [];
if (!data.content) return;
this.chapterContent = data.content;
this.initArags();
this.clearRangeContent();
document.querySelectorAll("pre code").forEach((el) => {
hljs.highlightElement(el);
});
await this.waitForContentLoad();
await this.replaceVideoToAudio();
if (scroll) {
window.scrollTo(0, document.documentElement.scrollHeight);
}
this.searchTextInSide();
this.upContentsHeight = $(".chapter-item-up").innerHeight();
await this.line_listFilterShow();
await this.toNoteIdSide();
this.listenHandlerEvents();
if (!offline) {
await this.uploadRead();
this.uploadReadInterval();
}
},
async getBookChapterData(chapterId, scroll) {
if (this.contentMode === "offline") return;
if (this.nextChapterLoading) return;
this.chapterContent = "";
window.scrollTo(0, 0);
this.nextChapterLoading = true;
const chapter_id = chapterId || this.chapter_id;
this.initArags(); this.initArags();
this.clearRangeContent(); this.clearRangeContent();
if (Object.entries(args).length > 0) { if (!this.book_id || !chapter_id) {
if (args && args.direction === 'scrollUp') { this.nextChapterLoading = false;
$(".chapter-content-list .show").css({ return;
transform: `translateY(-${this.upContentsHeight - this.windowHeight}px)`, }
WebkitTranform: `translateY(-${this.upContentsHeight - this.windowHeight}px)`, let url = `${this.baseURL}${getChapterContentURL}`;
}) try {
const res = await request.post(url, {
book_id: this.book_id,
chapter_id: chapter_id,
});
if (res && res.code === 200) {
await this.renderChapterData(res.data, scroll);
} else if (res && res.data && res.data.code === 3001) {
clearInterval(this.readTimer);
this.lockedContent = true;
} else { } else {
this.nextChapterLoading = true; console.log("getBookChapterData error:", JSON.stringify(res));
$("#chapter-content-list .show").css({
transform: `translateY(0px)`,
WebkitTranform: `translateY(0px)`,
});
} }
this.book_id = args.book_id; } catch (error) {
this.chapter_id = args.chapter_id; if (error && error.response && error.response.status === 403) {
this.next_chapter = args.next_chapter; // 下一节的内容 this.callHandlerNoticeFlutterSubmit("refreshTokenCallback");
this.prev_chapter = args.up_chapter; // 上一节的内容 } else {
this.color_line = args.color_line; // 高亮 console.log("getBookChapterData request error:", error);
this.line_list = args.line_list; // 划线 }
this.chapterContent = args.content; } finally {
this.nextChapterLoading = false;
this.color_line.forEach((item) => {
item.color_id = item.id;
})
this.line_list.forEach((item) => {
item.line_id = item.id;
})
this.callHandlerNoticeFlutterSubmit("dismissLoadingCallback");
document.querySelectorAll('pre code').forEach((el) => {
hljs.highlightElement(el);
});
// 带内容加载完成
await this.waitForContentLoad();
await this.replaceVideoToAudio();
setTimeout(() => {
// 计算设置上一节的高度
this.chapterContentHeight = $("#chapter-content-list .show").innerHeight();
this.upContentsHeight = $(".chapter-item-up").innerHeight();
}, 1000)
await this.line_listFilterShow();
await this.toNoteIdSide();
// 各类事件
this.listenHandlerEvents();
} else {
$("#chapter-content-list .show").css({
transform: `translateY(0px)`,
WebkitTranform: `translateY(0px)`,
});
} }
this.nextChapterLoading = false;
this.callHandlerNoticeFlutterSubmit("dismissLoadingCallback");
}, },
// 替换video标签
replaceVideoToAudio() { replaceVideoToAudio() {
const allVideo = document.querySelectorAll("video"); const allVideo = document.querySelectorAll("video");
let arr = ['.mp3', '.wma', '.wmv', '.aac', '.flac'] let arr = ['.mp3', '.wma', '.wmv', '.aac', '.flac']
...@@ -394,6 +233,80 @@ function countSubstringOccurrences(str, substring) { ...@@ -394,6 +233,80 @@ function countSubstringOccurrences(str, substring) {
}) })
} }
}, },
// 添加学习进度
async uploadReadProgress(chapter_id, progress) {
if (!this.token) return;
let url = `${this.baseURL}${getChapterContentProgressURL}`;
const chapterId = chapter_id || this.chapter_id;
const res = await request.post(url, {
book_id: this.book_id,
chapter_id: chapterId,
progress: progress,
});
},
// 添加阅读记录
async uploadReadRecord(chapter_id, progress) {
if (!this.token) return;
let url = `${this.baseURL}${getChapterContentRecordURL}`;
const chapterId = chapter_id || this.chapter_id;
const res = await request.post(url, {
book_id: this.book_id,
chapter_id: chapterId,
positioning: progress,
});
},
// 阅读进度上报
async uploadRead() {
let url = `${this.baseURL}${getChapterContentProgressURL}`;
// 章节内容不到没有撑满整个屏幕
let progress = 0;
if (this.chapterContentHeight <= this.docmHeight) {
progress = 100;
await this.uploadReadProgress(null, progress);
await this.uploadReadRecord(null, progress);
} else {
const visibleBottom = window.scrollY + window.innerHeight;
progress = parseInt((visibleBottom / this.chapterContentHeight) * 100);
progress = Math.min(progress, 100);
await this.uploadReadProgress(null, progress);
await this.uploadReadRecord(null, progress);
}
},
// 上报的定时器
uploadReadInterval() {
clearInterval(this.readTimer);
this.readTimer = setInterval(() => {
this.uploadRead();
}, 5000);
},
// 全文搜索设置
searchTextInSide() {
if (this.searchText) {
const allChildren = $(".chapter-item")[0].children;
let index = -1;
for (let i = 0; i < allChildren.length; i++) {
const textContent = allChildren[i].textContent;
if (textContent.indexOf(this.searchText) > -1) {
index = i;
break;
}
}
if (index >= 0) {
const currentTop = allChildren[index].offsetTop;
if (this.chapterContentHeight > this.docmHeight) {
let maxTop = this.chapterContentHeight - this.docmHeight;
if (currentTop >= maxTop) {
window.scrollTo(0, maxTop);
} else {
window.scrollTo(0, currentTop);
}
}
}
}
},
// 气泡操作 // 气泡操作
async tooltipOpa(elem) { async tooltipOpa(elem) {
const position = $(elem).data("random"); const position = $(elem).data("random");
...@@ -414,8 +327,12 @@ function countSubstringOccurrences(str, substring) { ...@@ -414,8 +327,12 @@ function countSubstringOccurrences(str, substring) {
} }
}, },
// 交互练习 // 交互练习
async practiceOpa(elem) { async practiceOpa(elem) {
// 交互练习 if (this.contentMode === "offline") {
window.alert("该内容需要联网后查看");
return;
}
// 交互练习
const position = $(elem).data("practicenum"); const position = $(elem).data("practicenum");
const title = $(elem).data("title"); const title = $(elem).data("title");
if (position) { if (position) {
...@@ -426,12 +343,17 @@ function countSubstringOccurrences(str, substring) { ...@@ -426,12 +343,17 @@ function countSubstringOccurrences(str, substring) {
position: position, position: position,
}); });
if (data.code === 200) { if (data.code === 200) {
this.callHandlerNoticeFlutterSubmit("answerResultCallBack", [ readerBridge.openWebview(
this.chapter_id, data.data.counts === "0" || data.data.counts === 0
position, ? "evaluating.html"
data.data.counts, : "evaluating_result.html",
title, {
]); book_id: this.book_id,
chapter_id: this.chapter_id,
position,
title,
}
);
} }
} }
}, },
...@@ -478,106 +400,34 @@ function countSubstringOccurrences(str, substring) { ...@@ -478,106 +400,34 @@ function countSubstringOccurrences(str, substring) {
observer.observe(document, { childList: true, subtree: true }); observer.observe(document, { childList: true, subtree: true });
}); });
}, },
// 监听touch事件 // 监听touch事件
async listenHandlerEvents() { listenHandlerEvents() {
const eleContent = document.querySelector("#chapter-content-list"); if (this.eventsBound) return;
const chapterContent = document.querySelector("#chapter-content-list"); this.eventsBound = true;
document.addEventListener("touchstart", this.touchStartEvent); document.addEventListener("touchstart", this.touchStartEvent);
document.addEventListener("touchmove", this.touchMoveEvent); document.addEventListener("touchend", this.touchEndEvent);
document.addEventListener("touchend", this.touchEndEvent); },
}, touchStartEvent(event) {
async touchStartEvent(event) {
event.stopPropagation(); event.stopPropagation();
const _that = this; const _that = this;
const target = event.target; const target = event.target;
const selection = window.getSelection();
_that.touchstartTime = Date.now(); _that.touchstartTime = Date.now();
_that.touchIn = true;
cancelAnimationFrame(_that.animationId);
_that.animationType = null;
_that.velocity = 0.5;
if ($(target).closest(".tange-tooptip").length > 0 ) { if ($(target).closest(".tange-tooptip").length > 0 ) {
event.preventDefault(); // event.preventDefault();
} else { } else {
_that.tooltipId = null; _that.tooltipId = null;
$(".tange-tooptip").css({ opacity: 0 }); $(".tange-tooptip").css({ opacity: 0 });
} }
// 使用正则表达式提取矩阵的最后两个值 _that.touchStartedAtTop = window.scrollY <= 0;
const transformValue = $("#chapter-content-list .show").css( _that.touchStartedAtBottom =
"transform" window.scrollY + window.innerHeight >=
); document.documentElement.scrollHeight - 1;
var matches = transformValue.match(
/matrix\(\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*\)/
);
_that.translateY = matches[6] ? parseInt(matches[6]) : 0;
_that.scrollPageY = _that.scrollPageY =
event.pageY || event.pageY ||
event.changedTouches[0].clientY || event.changedTouches[0].clientY ||
event.touches[0].clientY; event.touches[0].clientY;
_that.scrollLoading = true;
},
async touchMoveEvent(event) {
event.stopPropagation();
const _that = this;
const target = event.target;
if (_that.galleryIndex > 0) {
event.preventDefault();
return false;
}
if ($(target).closest(".tange-tooptip").length > 0 ) {
event.preventDefault();
return false;
}
const end =
event.pageY ||
event.changedTouches[0].clientY ||
event.touches[0].clientY;
const move = end - _that.scrollPageY;
if (_that.selectedText) return;
if (_that.touchIn) {
const endY =
event.pageY ||
event.changedTouches[0].clientY ||
event.touches[0].clientY;
const move = endY - _that.scrollPageY;
const lateY = _that.translateY + parseInt(move / 1);
$("#chapter-content-list .show").css({
transform: `translateY(${lateY}px)`,
WebkitTranform: `translateY(${lateY}px)`,
});
const mayScrollHeight = _that.chapterContentHeight - _that.docmHeight; // 可滚动的高
if (lateY > 0) {
// 触发下拉刷新
let opacity = 0;
if (lateY >= 100) {
opacity = 1;
} else if (lateY <= 50) {
opacity = 0;
} else {
opacity = (lateY / 50).toFixed(2);
}
$(".prev-chapter").css({ opacity: opacity });
} else if (lateY <= 0 && mayScrollHeight - Math.abs(lateY) >= 0) {
$(".prev-chapter, .next-chapter").css({ opacity: 0 });
} else if (Math.abs(lateY) - mayScrollHeight > 0) {
// 触发上拉加载
let maxLateY = Math.abs(lateY) - mayScrollHeight;
if (maxLateY >= 100) {
opacity = 1;
} else if (lateY <= 50) {
opacity = 0;
} else {
opacity = (maxLateY / 50).toFixed(2);
}
$(".next-chapter").css({ opacity: opacity });
}
}
}, },
async touchEndEvent(event) { async touchEndEvent(event) {
event.stopPropagation(); event.stopPropagation();
...@@ -590,7 +440,6 @@ function countSubstringOccurrences(str, substring) { ...@@ -590,7 +440,6 @@ function countSubstringOccurrences(str, substring) {
event.changedTouches[0].clientY || event.changedTouches[0].clientY ||
event.touches[0].clientY; event.touches[0].clientY;
// 处理双击事件 // 处理双击事件
if ( if (
_that.touchLastClickTime && _that.touchLastClickTime &&
...@@ -604,7 +453,6 @@ function countSubstringOccurrences(str, substring) { ...@@ -604,7 +453,6 @@ function countSubstringOccurrences(str, substring) {
return false; return false;
} }
_that.touchLastClickTime = endTime; _that.touchLastClickTime = endTime;
_that.touchIn = false;
if ($(target).closest(".tange-tooptip").length > 0 ) { if ($(target).closest(".tange-tooptip").length > 0 ) {
const href = $(target).closest(".c-link").attr("href"); const href = $(target).closest(".c-link").attr("href");
...@@ -640,20 +488,18 @@ function countSubstringOccurrences(str, substring) { ...@@ -640,20 +488,18 @@ function countSubstringOccurrences(str, substring) {
// 上一章,下一章 // 上一章,下一章
const move = endY - _that.scrollPageY; const move = endY - _that.scrollPageY;
const lateY = _that.translateY + parseInt(move / 2);
const mayScrollHeight = _that.chapterContentHeight - _that.docmHeight; // 可滚动的高
if (endTime - _that.touchstartTime <= 300) { if (endTime - _that.touchstartTime <= 300) {
// 创建 Range 对象 // 创建 Range 对象
const range = document.createRange(); const range = document.createRange();
const selection = window.getSelection(); const selection = window.getSelection();
if (Math.abs(move) <= 30) { if (Math.abs(move) <= 30) {
// 点击事件 // 点击事件
if (target.nodeName.toLowerCase() === "span") { if (target.nodeName.toLowerCase() === "span") {
// 点击的气泡内容 // 点击的气泡内容
if ($(target).closest(".chapter-item-tooltip").length > 0) { if ($(target).closest(".chapter-item-tooltip").length > 0) {
// _that.tooltipOpa($(target).closest(".chapter-item-tooltip")); _that.tooltipOpa($(target).closest(".chapter-item-tooltip"));
event.preventDefault(); event.preventDefault();
} else if ($(target).closest(".colorLine").length > 0) { } else if ($(target).closest(".colorLine").length > 0) {
this.boolControllStatus = false; this.boolControllStatus = false;
...@@ -699,6 +545,10 @@ function countSubstringOccurrences(str, substring) { ...@@ -699,6 +545,10 @@ function countSubstringOccurrences(str, substring) {
} }
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
} else if ($(target).closest(".lock-button").length) {
_that.callHandlerNoticeFlutterSubmit('payCallback');
event.stopPropagation();
event.preventDefault();
} }
} else if (target.nodeName.toLowerCase() === "a") { } else if (target.nodeName.toLowerCase() === "a") {
const href = $(target).attr("href"); const href = $(target).attr("href");
...@@ -743,22 +593,28 @@ function countSubstringOccurrences(str, substring) { ...@@ -743,22 +593,28 @@ function countSubstringOccurrences(str, substring) {
// 画廊 // 画廊
const title = $(target).closest(".chapter-gallery-container").data("title"); const title = $(target).closest(".chapter-gallery-container").data("title");
const position = $(target).closest(".chapter-gallery-container").data("random"); const position = $(target).closest(".chapter-gallery-container").data("random");
_that.callHandlerNoticeFlutterSubmit("readInfoCallback", [ readerBridge.openWebview("read_info.html", {
position, book_id: _that.book_id,
"gallery", chapter_id: _that.chapter_id,
title, position,
]); type: "gallery",
title,
});
} else if ($(target).closest(".chapter-item-tooltip").length > 0) { } else if ($(target).closest(".chapter-item-tooltip").length > 0) {
// _that.tooltipOpa($(target).closest(".chapter-item-tooltip")); _that.tooltipOpa(
$(target).closest(".chapter-item-tooltip")
);
} else if ($(target).closest(".chapter-expand").length > 0) { } else if ($(target).closest(".chapter-expand").length > 0) {
// 扩展 // 扩展
const title = $(target).closest(".chapter-expand").data("name"); const title = $(target).closest(".chapter-expand").data("name");
const position = $(target).closest(".chapter-expand").data("random"); const position = $(target).closest(".chapter-expand").data("random");
_that.callHandlerNoticeFlutterSubmit("readInfoCallback", [ readerBridge.openWebview("read_info.html", {
position, book_id: _that.book_id,
"expand", chapter_id: _that.chapter_id,
title, position,
]); type: "expand",
title,
});
} else if ($(target).closest(".c-link").length > 0) { } else if ($(target).closest(".c-link").length > 0) {
// 气泡链接 // 气泡链接
const href = $(target).closest(".c-link").attr("href"); const href = $(target).closest(".c-link").attr("href");
...@@ -770,7 +626,9 @@ function countSubstringOccurrences(str, substring) { ...@@ -770,7 +626,9 @@ function countSubstringOccurrences(str, substring) {
} else if (target.nodeName.toLowerCase() === "img") { } else if (target.nodeName.toLowerCase() === "img") {
// 点击的气泡内容 // 点击的气泡内容
if ($(target).closest(".chapter-item-tooltip").length > 0) { if ($(target).closest(".chapter-item-tooltip").length > 0) {
// _that.tooltipOpa($(target).closest(".chapter-item-tooltip")); _that.tooltipOpa(
$(target).closest(".chapter-item-tooltip")
);
} else if ( } else if (
$(target).closest(".chapter-gallery-container").length $(target).closest(".chapter-gallery-container").length
) { ) {
...@@ -781,26 +639,28 @@ function countSubstringOccurrences(str, substring) { ...@@ -781,26 +639,28 @@ function countSubstringOccurrences(str, substring) {
const position = $(target) const position = $(target)
.closest(".chapter-gallery-container") .closest(".chapter-gallery-container")
.data("random"); .data("random");
_that.callHandlerNoticeFlutterSubmit("readInfoCallback", [ readerBridge.openWebview("read_info.html", {
position, book_id: _that.book_id,
"gallery", chapter_id: _that.chapter_id,
title, position,
]); type: "gallery",
title,
});
} else { } else {
// 图片展示 // 图片展示
const scaleImg = $(target).attr("src"); const scaleImg = $(target).attr("src");
const title = $(target).attr("alt") || "图片展示"; const title = $(target).attr("alt") || "图片展示";
_that.callHandlerNoticeFlutterSubmit("scaleImageCallback", [ readerBridge.openWebview("read_img.html", {
scaleImg, url: scaleImg,
title, title,
]); });
} }
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
} else { } else {
if ($(target).closest(".chapter-practice").length > 0) { if ($(target).closest(".chapter-practice").length > 0) {
const ele = $(target).closest(".chapter-practice"); const ele = $(target).closest(".chapter-practice");
// _that.practiceOpa(ele); _that.practiceOpa(ele);
event.preventDefault(); event.preventDefault();
} else if (target.nodeName.toLowerCase() === "line") { } else if (target.nodeName.toLowerCase() === "line") {
let className = target.getAttribute("class"); let className = target.getAttribute("class");
...@@ -899,11 +759,13 @@ function countSubstringOccurrences(str, substring) { ...@@ -899,11 +759,13 @@ function countSubstringOccurrences(str, substring) {
const position = $(target) const position = $(target)
.closest(".chapter-gallery-container") .closest(".chapter-gallery-container")
.data("random"); .data("random");
_that.callHandlerNoticeFlutterSubmit("readInfoCallback", [ readerBridge.openWebview("read_info.html", {
position, book_id: _that.book_id,
"gallery", chapter_id: _that.chapter_id,
title, position,
]); type: "gallery",
title,
});
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
...@@ -911,11 +773,13 @@ function countSubstringOccurrences(str, substring) { ...@@ -911,11 +773,13 @@ function countSubstringOccurrences(str, substring) {
// 扩展 // 扩展
const title = $(target).closest(".chapter-expand").data("name"); const title = $(target).closest(".chapter-expand").data("name");
const position = $(target).closest(".chapter-expand").data("random"); const position = $(target).closest(".chapter-expand").data("random");
_that.callHandlerNoticeFlutterSubmit("readInfoCallback", [ readerBridge.openWebview("read_info.html", {
position, book_id: _that.book_id,
"expand", chapter_id: _that.chapter_id,
title, position,
]); type: "expand",
title,
});
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
...@@ -930,69 +794,36 @@ function countSubstringOccurrences(str, substring) { ...@@ -930,69 +794,36 @@ function countSubstringOccurrences(str, substring) {
} }
} }
} }
if (Math.abs(move) > 0) { }
if (move > 0) { if (!_that.selectedText) {
_that.animationType = "down"; if (
} else if (move < 0) { _that.touchStartedAtTop &&
_that.animationType = "up"; move >= 200 &&
} Object.entries(_that.prev_chapter).length > 0
_that.animationScroll(); ) {
} _that.chapter_id = _that.prev_chapter.id;
$(".prev-chapter, .next-chapter").css({ opacity: 0 }); _that.callHandlerNoticeFlutterSubmit("loadChapterCallBack", [
} else { _that.chapter_id,
_that.prev_chapter.name,
if (_that.selectedText) return; "scrollUp",
if (lateY > 0) { ]);
if (move >= 200) { await _that.getBookChapterData(null, true);
// 下拉刷新开始 } else if (
if (Object.entries(_that.prev_chapter).length > 0) { _that.touchStartedAtBottom &&
$(".prev-chapter").css({ opacity: 0 }); move <= -200 &&
_that.chapter_id = _that.prev_chapter.id; Object.entries(_that.next_chapter).length > 0
_that.chapterContent = _that.prev_chapter.contents; ) {
_that.callHandlerNoticeFlutterSubmit( clearInterval(_that.readTimer);
"loadChapterCallBack", _that.uploadReadProgress(null, 100);
[_that.chapter_id, _that.prev_chapter.name, 'scrollUp'] _that.uploadReadRecord(null, 100);
); _that.uploadReadInterval();
} else { _that.chapter_id = _that.next_chapter.id;
$(".prev-chapter").css({ opacity: 0 }); _that.callHandlerNoticeFlutterSubmit("loadChapterCallBack", [
$("#chapter-content-list .show").css({ _that.chapter_id,
transform: `translateY(${0}px)`, _that.next_chapter.name,
WebkitTranform: `translateY(${0}px)`, ]);
}); await _that.getBookChapterData();
}
} else {
$(".prev-chapter").css({ opacity: 0 });
$("#chapter-content-list .show").css({
transform: `translateY(${0}px)`,
WebkitTranform: `translateY(${0}px)`,
});
}
} else if (lateY < 0) {
if (Math.abs(lateY) >= mayScrollHeight) {
if (Math.abs(move) >= 200) {
if (Object.entries(_that.next_chapter).length > 0) {
$(".next-chapter").css({ opacity: 0 });
_that.chapter_id = _that.next_chapter.id;
_that.callHandlerNoticeFlutterSubmit(
"loadChapterCallBack",
[_that.chapter_id, _that.next_chapter.name]
);
}
} else {
$(".next-chapter").css({ opacity: 0 });
$("#chapter-content-list .show").css({
transform: `translateY(-${mayScrollHeight}px)`,
WebkitTranform: `translateY(-${mayScrollHeight}px)`,
});
}
}
} else {
$("#chapter-content-list .show").css({
transform: `translateY(${0}px)`,
WebkitTranform: `translateY(${0}px)`,
});
} }
_that.scrollLoading = false;
} }
// 防抖函数 // 防抖函数
...@@ -1012,48 +843,6 @@ function countSubstringOccurrences(str, substring) { ...@@ -1012,48 +843,6 @@ function countSubstringOccurrences(str, substring) {
}, 150); }, 150);
}, },
// 缓冲动画设置
animationScroll() {
if (!this.touchIn) {
const transformValue = $("#chapter-content-list .show").css(
"transform"
);
var matches = transformValue.match(
/matrix\(\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*\)/
);
let translateY = matches[6] ? parseInt(matches[6]) : 0;
// 根据摩擦系数逐渐减小速度
this.velocity *= 1 - this.friction;
let tempY = 0;
if (this.animationType === "up") {
tempY = translateY - this.velocity * 75;
} else if (this.animationType === "down") {
tempY = translateY + this.velocity * 75;
}
if (tempY > 0) {
tempY = 0;
} else if (
Math.abs(tempY) >=
this.chapterContentHeight - this.docmHeight
) {
tempY = -(this.chapterContentHeight - this.docmHeight);
}
$("#chapter-content-list .show").css({
transform: `translateY(${tempY}px)`,
WebkitTranform: `translateY(${tempY}px)`,
});
// 如果速度足够小,停止动画
if (Math.abs(this.velocity) <= 0.1) {
cancelAnimationFrame(this.animationId);
this.animationType = null;
this.velocity = 0.6;
return;
}
// 执行下一帧动画
this.animationId = requestAnimationFrame(this.animationScroll);
}
},
// range 设置 // range 设置
async selectionRangeOperation() { async selectionRangeOperation() {
const _that = this; const _that = this;
...@@ -1298,12 +1087,22 @@ function countSubstringOccurrences(str, substring) { ...@@ -1298,12 +1087,22 @@ function countSubstringOccurrences(str, substring) {
this.rangePosition = { type: this.selectRangeType, info: rangeInformations }; this.rangePosition = { type: this.selectRangeType, info: rangeInformations };
}, },
// 点击讨论、笔记、百科 // 点击讨论、笔记、百科、字典
async rangeTextInFlutter(type) { async rangeTextInFlutter(type) {
if (this.selectedText && type) { if (this.selectedText && type) {
this.callHandlerNoticeFlutterSubmit( if (type === "baike") {
`${type}CallBack`, return readerBridge.openWebview(
this.selectedText `https://baike.baidu.com/item/${encodeURIComponent(this.selectedText)}?fromModule=lemma_search-box`
);
}
if (type === "dict") {
return readerBridge.openWebview(
`https://dict.baidu.com/s?wd=${encodeURIComponent(this.selectedText)}`
);
}
this.callHandlerNoticeFlutterSubmit(
`${type}CallBack`,
this.selectedText
); );
} }
}, },
...@@ -1372,8 +1171,15 @@ function countSubstringOccurrences(str, substring) { ...@@ -1372,8 +1171,15 @@ function countSubstringOccurrences(str, substring) {
return; return;
} }
this.nodeType = 'line'; const data = await this.addNotes(1);
await this.addNotes(1); if (!data.id) return;
this.nowLineId = data.id;
this.boolControllStatus = true;
this.boolLightColor = false;
this.findSelectionRangeFill("line");
// 删除之前的
this.delOldLineAndLightIds();
}, },
// 高亮设置 // 高亮设置
async rangeTextInLight() { async rangeTextInLight() {
...@@ -1456,10 +1262,16 @@ function countSubstringOccurrences(str, substring) { ...@@ -1456,10 +1262,16 @@ function countSubstringOccurrences(str, substring) {
this.boolLightColor = true; this.boolLightColor = true;
if (bool) { if (bool) {
await this.addNotes(2); data = await this.addNotes(2);
if (!data.id) return;
this.nowColorId = data.id;
this.findSelectionRangeFill("light");
// 删除之前的
this.delOldLineAndLightIds();
return; return;
} else { } else {
await this.editNotes(2); data = await this.editNotes(2);
if (data.is_success != 1) return;
const all = document.querySelectorAll(`light.light-none-${this.nowColorId}`); const all = document.querySelectorAll(`light.light-none-${this.nowColorId}`);
all.forEach((item) => { all.forEach((item) => {
$(item).removeClass().addClass(`light-none-${this.nowColorId} c-${color}`); $(item).removeClass().addClass(`light-none-${this.nowColorId} c-${color}`);
...@@ -1568,60 +1380,113 @@ function countSubstringOccurrences(str, substring) { ...@@ -1568,60 +1380,113 @@ function countSubstringOccurrences(str, substring) {
selection.removeAllRanges(); selection.removeAllRanges();
this.tempCloneRange = null; this.tempCloneRange = null;
}, },
// 添加划线、高亮 // 添加划线、高亮
async addNotes(type) { async addNotes(type) {
let positioning = JSON.stringify(this.rangePosition); let positioning = JSON.stringify(this.rangePosition);
let data = { let data = {
book_id: this.book_id, book_id: this.book_id,
chapter_id: this.chapter_id, chapter_id: this.chapter_id,
types: type, types: type,
color: type === 2 ? `#${this.nowActiveColor}` : "#ab1941", color: type === 2 ? `#${this.nowActiveColor}` : "#ab1941",
content: this.selectedText, content: this.selectedText,
positioning: positioning, positioning: positioning,
}; };
this.notesType = type === 1 ? 'line' : 'light';
this.callHandlerNoticeFlutterSubmit(`offlineAddNoteCallBack`, { ...data, notes_id: 0 }); if (this.contentMode === "offline") {
this.notesType = type === 1 ? "line" : "light";
this.callHandlerNoticeFlutterSubmit("offlineAddNoteCallBack", {
...data,
notes_id: 0,
});
return data;
}
let url = `${this.baseURL}${addInfomationNotes}`;
const res = await request.post(url, data);
if (res.code === 200) {
this.callHandlerNoticeFlutterSubmit(`offlineAddNoteCallBack`, { ...data, notes_id: res.data.id});
return res.data;
}
}, },
// 编辑划线、高亮 // 编辑划线、高亮
async editNotes(type, expand, notes_id) { async editNotes(type, expand, notes_id) {
let id = null; let id = null;
if (expand) { if (expand) {
id = notes_id; id = notes_id;
} else { } else {
id = type === 2 ? this.nowColorId : this.nowLineId; id = type === 2 ? this.nowColorId : this.nowLineId;
} }
let data = {
let data = {
book_id: this.book_id, book_id: this.book_id,
chapter_id: this.chapter_id, chapter_id: this.chapter_id,
notes_id: 0, notes_id: id,
types: type, types: type,
color: type === 2 ? `#${this.nowActiveColor}` : "", color: type === 2 ? `#${this.nowActiveColor}` : "",
content: this.selectedText, content: this.selectedText,
positioning: JSON.stringify(this.rangePosition), positioning: JSON.stringify(this.rangePosition),
}; };
this.callHandlerNoticeFlutterSubmit(`offlineUpdateNoteCallBack`, { ...data, id: id });
return { is_success: 1}; if (this.contentMode === "offline") {
this.callHandlerNoticeFlutterSubmit("offlineUpdateNoteCallBack", {
...data,
id,
});
return { is_success: 1 };
}
let url = `${this.baseURL}${editInfomationNotes}`;
const res = await request.post(url, data);
if (res.code === 200) {
this.callHandlerNoticeFlutterSubmit(`offlineUpdateNoteCallBack`, { ...data });
return res.data;
}
}, },
// 删除划线、高亮 // 删除划线、高亮
async delNotes(type, id) { async delNotes(type, id) {
let data = { let data = {
book_id: this.book_id, book_id: this.book_id,
chapter_id: this.chapter_id, chapter_id: this.chapter_id,
notes_id: 0 notes_id: id
}; ? id
this.callHandlerNoticeFlutterSubmit(`offlineDelNoteCallBack`, { ...data, id : type === "line" ? this.nowLineId : this.nowColorId }); : type === "line"
return { is_success: 1 }; ? this.nowLineId
}, : this.nowColorId,
};
if (this.contentMode === "offline") {
this.callHandlerNoticeFlutterSubmit("offlineDelNoteCallBack", {
...data,
id: data.notes_id,
});
return { is_success: 1 };
}
let url = `${this.baseURL}${delInfomationNotes}`;
const res = await request.post(url, data);
this.callHandlerNoticeFlutterSubmit("offlineDelSyncedNotesCallBack", [
data.notes_id,
]);
return res.data;
},
// 批量删除划线、高亮 // 批量删除划线、高亮
async delBatchNotes(notes) { async delBatchNotes(notes) {
if (this.contentMode === "offline") {
this.callHandlerNoticeFlutterSubmit(
"offlineDelLocalNotesCallBack",
notes
);
return { is_success: 1 };
}
let url = `${this.baseURL}${delBatchInfomationNotes}`;
let data = { let data = {
book_id: this.book_id, book_id: this.book_id,
chapter_id: this.chapter_id, chapter_id: this.chapter_id,
notes_ids: notes.join(","), notes_ids: notes.join(","),
}; };
const res = await request.post(url, data);
this.callHandlerNoticeFlutterSubmit(`offlineDelNoteCallBack`, { ...data }); this.callHandlerNoticeFlutterSubmit(`offlineDelNoteCallBack`, { ...data });
return { is_success: 1}; return res.data;
}, },
// 删除之前的内容 // 删除之前的内容
async delOldLineAndLightIds () { async delOldLineAndLightIds () {
...@@ -1971,7 +1836,6 @@ function countSubstringOccurrences(str, substring) { ...@@ -1971,7 +1836,6 @@ function countSubstringOccurrences(str, substring) {
WebkitTranform: `translateY(0px)`, WebkitTranform: `translateY(0px)`,
}); });
this.rangeMenuStatus = false; this.rangeMenuStatus = false;
this.notesType = '';
}, },
getLineHeightInPixels(element) { getLineHeightInPixels(element) {
const computedStyle = window.getComputedStyle(element); const computedStyle = window.getComputedStyle(element);
...@@ -2008,17 +1872,13 @@ function countSubstringOccurrences(str, substring) { ...@@ -2008,17 +1872,13 @@ function countSubstringOccurrences(str, substring) {
} }
}, },
// 接收到了高亮或划线的id // 接收到了高亮或划线的id
toNoteIdSide() { toNoteIdSide() {
if (this.noteId > 0) { if (this.noteId > 0) {
$("line, light").each((index, item) => { $("line, light").each((index, item) => {
const id = $(item).data("id"); const id = $(item).data("id");
if (parseInt(id) === parseInt(this.noteId)) { if (parseInt(id) === parseInt(this.noteId)) {
const idSide = $(item)[0].getBoundingClientRect(); const idSide = $(item)[0].getBoundingClientRect();
const sideTop = idSide.top - 50; window.scrollTo(0, window.scrollY + idSide.top - 50);
$("#chapter-content-list .show").css({
transform: `translateY(-${sideTop}px)`,
WebkitTranform: `translateY(-${sideTop}px)`,
});
} }
}); });
this.noteId = -1; this.noteId = -1;
...@@ -2026,46 +1886,109 @@ function countSubstringOccurrences(str, substring) { ...@@ -2026,46 +1886,109 @@ function countSubstringOccurrences(str, substring) {
$("video, audio").each((index, item) => { $("video, audio").each((index, item) => {
$(item).attr("controlslist", "nodownload"); $(item).attr("controlslist", "nodownload");
}); });
}, },
addSuccessCallBack(id) { addSuccessCallBack(id) {
if (this.notesType === 'line') { if (this.notesType === "line") {
this.nowLineId = id; this.nowLineId = id;
this.boolControllStatus = true; this.boolControllStatus = true;
this.boolLightColor = false; this.boolLightColor = false;
this.findSelectionRangeFill("line"); this.findSelectionRangeFill("line");
} else { } else {
this.nowColorId = id; this.nowColorId = id;
this.findSelectionRangeFill("light"); this.findSelectionRangeFill("light");
}
this.delOldLineAndLightIds();
},
startOnlineReader(data) {
this.contentMode = "online";
this.offlinePayload = null;
this.baseURL = DEFAULT_API_BASE_URL;
this.book_id = data.book_id || "";
this.chapter_id = data.chapter_id || "";
this.token = data.token || localStorage.getItem("token") || "";
this.noteId = data.note_id || -1;
this.searchText = data.search_text || "";
localStorage.setItem("token", this.token);
setTimeout(async () => {
await this.getBookChapterData();
}, 200);
},
// flutter 回调
async callbackInFlutterComponent(args) {
try {
if (isOfflinePayload(args)) {
const payload = normalizeOfflinePayload(args);
this.contentMode = "offline";
this.offlinePayload = payload;
this.book_id = payload.book_id;
this.chapter_id = payload.chapter_id;
this.noteId = payload.note_id || -1;
await this.renderChapterData(
payload,
payload.direction === "scrollUp",
true
);
return;
}
if (args && typeof args === "object") {
this.startOnlineReader(args);
return;
}
const data = args.split(",");
const offset = data.length >= 6 ? 1 : 0;
this.startOnlineReader({
book_id: data[offset],
chapter_id: data[offset + 1],
token: data[offset + 2],
note_id: data[offset + 3],
search_text: data[offset + 4],
});
} catch (e) {
console.log(e);
} }
// 删除之前的
this.delOldLineAndLightIds();
}, },
// flutter 回调 async refreshTokenSuccess(args) {
async callbackInFlutterComponent(args) { try {
this.getBookChapterData(args); this.token = args.token;
}, localStorage.setItem("token", args.token);
// 通知flutter组件 this.getBookChapterData();
callHandlerNoticeFlutterSubmit(funcname = "evaluatingSubmit", data) { } catch (e) {
if ( console.log(e);
window.flutter_inappwebview &&
window.flutter_inappwebview.callHandler
) {
window.flutter_inappwebview.callHandler(funcname, data).then(function (result) {
console.log("Result from Flutter:", result);
});
} }
}, },
// 通知flutter组件
callHandlerNoticeFlutterSubmit(funcname = "evaluatingSubmit", data) {
if (this.contentMode === "offline" && funcname === "openLinkCallback") {
window.alert("该内容需要联网后查看");
return Promise.resolve();
}
return readerBridge.call(funcname, data);
},
}, },
}); });
const callbackInFlutterComponent = (arguments) => { const callbackInFlutterComponent = (args) => {
return app.callbackInFlutterComponent(arguments); return app.callbackInFlutterComponent(args);
}; };
const addSuccessCallBack = (arguments) => { const refreshTokenSuccess = (args) => {
return app.addSuccessCallBack(arguments); return app.refreshTokenSuccess(args);
}; };
window.callbackInFlutterComponent = callbackInFlutterComponent;
</script> window.refreshTokenSuccess = refreshTokenSuccess;
</body> window.addSuccessCallBack = (id) => app.addSuccessCallBack(id);
</html> const urlParams = new URLSearchParams(window.location.search);
const bookId = urlParams.get("book_id");
const chapterId = urlParams.get("chapter_id");
if (bookId && chapterId && urlParams.get("source") !== "app") {
app.callbackInFlutterComponent({
book_id: bookId,
chapter_id: chapterId,
token: urlParams.get("token") || "",
note_id: urlParams.get("note_id") || "-1",
search_text: urlParams.get("search_text") || "",
});
}
// callbackInFlutterComponent(',175,335,2f85d063f94485c0c2ef31e652875bd5');
export const readerApi = {
post(url, data) {
if (!window.readerRequest || typeof window.readerRequest.post !== "function") {
throw new Error("readerRequest is not ready");
}
return window.readerRequest.post(url, data);
},
};
export const readerBridge = {
call(name, data) {
const bridge = window.flutter_inappwebview;
if (!bridge || typeof bridge.callHandler !== "function") {
return Promise.resolve(undefined);
}
return Promise.resolve(bridge.callHandler(name, data)).catch((error) => {
console.warn(`Flutter callback failed: ${name}`, error);
return undefined;
});
},
openWebview(page, params = {}) {
if (window.location.protocol === "file:") {
window.alert("该内容需要联网后查看");
return Promise.resolve();
}
const url = new URL(page, window.location.href);
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null && value !== "") {
url.searchParams.set(key, value);
}
});
return this.call("openWebview", url.toString());
},
};
export const READER_ENDPOINTS = {
chapterContent: "/v1/book/Information/getSectionContent",
chapterProgress: "/v1/book/Information/addProgress",
chapterRecord: "/v1/book/Information/addReadRecord",
addNotes: "/v1/book/Information/addNotes",
editNotes: "/v1/book/Information/editNotes",
deleteNotes: "/v1/book/Information/delNotes",
deleteNotesBatch: "/v1/book/Information/delListNotes",
checkPractice: "/v1/book/Information/checkChapterQuestion",
checkTooltip: "/v1/book/Information/getBubbleByPosition",
};
export const DEFAULT_API_BASE_URL =
typeof window !== "undefined" && window.BOOK_API_BASE_URL
? window.BOOK_API_BASE_URL
: "https://project-api.ezijing.com/api/book";
export function normalizeOfflinePayload(payload) {
return {
...payload,
book_id: payload.book_id || payload.bookId || "",
chapter_id: payload.chapter_id || payload.chapterId || "",
content: payload.content || "",
next_chapter: payload.next_chapter || {},
up_chapter: payload.up_chapter || {},
color_line: Array.isArray(payload.color_line) ? payload.color_line : [],
line_list: Array.isArray(payload.line_list) ? payload.line_list : [],
};
}
export function isOfflinePayload(value) {
return Boolean(value && typeof value === "object" && "content" in value);
}
...@@ -8,21 +8,26 @@ const __dirname = dirname(__filename) ...@@ -8,21 +8,26 @@ const __dirname = dirname(__filename)
// 获取所有 HTML 文件作为入口 // 获取所有 HTML 文件作为入口
function getHtmlEntries() { function getHtmlEntries() {
const htmlFiles = glob.sync('*.html', { cwd: __dirname }) const sourceDirectory = resolve(__dirname, 'src')
const htmlFiles = glob.sync('*.html', { cwd: sourceDirectory })
const entries = {} const entries = {}
htmlFiles.forEach((file) => { htmlFiles.forEach((file) => {
const name = file.replace(/\.html$/, '') const name = file.replace(/\.html$/, '')
entries[name] = resolve(__dirname, file) entries[name] = resolve(sourceDirectory, file)
}) })
return entries return entries
} }
export default defineConfig({ export default defineConfig({
root: '.', root: resolve(__dirname, 'src'),
publicDir: resolve(__dirname, 'public'),
base: './',
build: { build: {
outDir: 'dist', outDir: resolve(__dirname, 'dist'),
emptyOutDir: true,
manifest: true,
rollupOptions: { rollupOptions: {
input: getHtmlEntries(), input: getHtmlEntries(),
output: { output: {
...@@ -54,7 +59,7 @@ export default defineConfig({ ...@@ -54,7 +59,7 @@ export default defineConfig({
}, },
resolve: { resolve: {
alias: { alias: {
'@': resolve(__dirname, './'), '@': resolve(__dirname, 'src'),
}, },
}, },
}) })
Markdown 格式
0% 或
您添加了 0 人 到此讨论。请谨慎行事。
请先完成此评论的编辑!
请 注册 或者 后发表评论