commit ea760bb71ca14d46e6a93e27e4a258b3dac1eb54 Author: Daniel Date: Sat Mar 7 19:13:49 2026 +0800 feat: new projiect diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b625224 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# 全景图体积较大,按需提交 +panorama/*.jpg +panorama/*.jpeg +panorama/*.png +panorama/*.webp +!panorama/.gitkeep + +# Node +node_modules/ +dist/ +data/ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3b66410 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "git.ignoreLimitWarning": true +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..49e1086 --- /dev/null +++ b/README.md @@ -0,0 +1,162 @@ +# 720yun 全景本地化 / 离线部署 + +将 [720云](https://www.720yun.com) VR 全景页面内容抓取并本地化,实现**完全离线**部署与查看。 + +目标示例:`https://www.720yun.com/vr/c8525usOunr` + +--- + +## 项目结构 + +``` +720yun-offline/ +├── index.html # 本地全景查看页(Pannellum) +├── config.json # 全景配置(默认使用 image/ 六面图) +├── lib/ +│ ├── pannellum.js +│ └── pannellum.css +├── image/ # 六面体全景图(mobile_f/r/b/l/u/d.jpg),默认展示来源 +├── panorama/ # 单张全景图(可选) +├── server.js # Node 静态 + API 服务(统计、留言、弹幕) +├── db.js # SQLite 封装(data/pano.db) +├── data/ # 数据库目录(自动创建,已 gitignore) +├── build.js # 构建脚本,产出 dist/ 便于部署 +├── package.json # Node 脚本:start / build / preview +├── fetch_720yun.py # 自动抓取脚本(若页面有直出 URL 则可用) +├── parse_720yun_doc.py # 从 text.md 解析 URL,--download 下载到 image/ +└── README.md +``` + +--- + +## 一、获取 720yun 全景图(二选一) + +### 方式 A:自动抓取(仅当页面 HTML 里直接包含图片 URL 时有效) + +```bash +cd 720yun-offline +python3 fetch_720yun.py "https://www.720yun.com/vr/c8525usOunr" +``` + +若输出「未在页面 HTML 中发现全景图 URL」,说明该页由前端 JS 请求接口加载数据,请用方式 B。 + +### 方式 B:手动获取(推荐,适用于所有 720 链接) + +1. 在浏览器中打开目标链接: + `https://www.720yun.com/vr/c8525usOunr` +2. 打开开发者工具:**F12**(或 右键 → 检查)→ 切到 **Network(网络)**。 +3. 勾选 **Disable cache**,刷新页面(F5)。 +4. 在筛选里选择 **Img** 或 **XHR/Fetch**: + - **Img**:找尺寸很大的图片(如 4096×2048、8192×4096 等),即全景图。 + - **XHR/Fetch**:找返回 JSON 的接口(如含 `scene`、`panorama`、`url` 等字段),从返回里拿到全景图 URL。 +5. 在找到的图片或接口响应里复制**图片 URL**,在浏览器新标签页打开该 URL,**右键 → 图片另存为**,保存到本项目的 `panorama/` 目录,命名为 `panorama.jpg`(或其它名字,见下方配置)。 + +若有多场景(多个全景图),可保存为 `panorama/scene1.jpg`、`panorama/scene2.jpg` 等,并在 `config.json` 的 `scenes` 中配置多场景(见 Pannellum 文档)。 + +--- + +## 二、Node 构建与部署(推荐) + +项目已用 Node 方式组织,**默认使用 `image/` 下六面图**(由 `parse_720yun_doc.py --download` 拉取),打开页面即自动加载。 + +### 1. 安装与运行(开发/本地) + +```bash +cd 720yun-offline +npm install +npm start +``` + +浏览器访问 **http://localhost:3000**,会自动加载 `config.json` 中的立方体六面图(`image/mobile_*.jpg`)。 +服务端会创建 **`data/pano.db`**(SQLite),用于存储:**累积播放、实时在看、点赞数、分享数、留言(弹幕)**。留言以弹幕形式在画面上方滚动播放。 + +### 2. 构建出站目录(部署用) + +```bash +npm run build +``` + +会将 `index.html`、`config.json`、`lib/`、`image/` 复制到 **`dist/`**。若需保留统计与弹幕功能,需将 **Node 服务(server.js + db.js + data/)** 一并部署;仅部署静态 `dist/` 时,前端会请求 `/api` 失败,观看/点赞/分享/留言不落库。 + +### 3. 预览构建结果 + +```bash +npm run preview +``` + +先执行 `npm run build`,再以 `dist/` 为根目录启动服务器,用于验证部署包。 + +### 4. 确认/修改 `config.json` + +默认已配置为使用 **立方体六面图**(`image/` 下 mobile_f → mobile_d): + +```json +{ + "type": "cubemap", + "cubeMap": [ + "image/mobile_f.jpg", + "image/mobile_r.jpg", + "image/mobile_b.jpg", + "image/mobile_l.jpg", + "image/mobile_u.jpg", + "image/mobile_d.jpg" + ], + "title": "720° 全景离线", + "autoLoad": true, + ... +} +``` + +若使用单张 2:1 全景图,可改为 `"type": "equirectangular"`、`"panorama": "panorama/panorama.jpg"`。 + +### 5. 其它方式起 HTTP 服务 + +因浏览器安全限制,不能直接用 `file://` 打开。除 `npm start` 外也可: + +**Python 3:** + +```bash +python3 -m http.server 8080 +``` + +**npx serve:** + +```bash +npx serve -p 8080 +``` + +### 6. 离线部署到其它机器 + +将整个项目(或仅 `dist/`)拷贝到 U 盘或内网服务器,在目标机器上执行 `npm start` 或部署 `dist/` 到 Web 服务器即可,无需外网。 + +--- + +## 三、多场景与热点(可选) + +若 720 原页有多场景或热点,可参考 [Pannellum 配置说明](https://pannellum.org/documentation/reference/): + +- **多场景**:在 `config.json` 中使用 `default.firstScene` + `scenes`,每个 scene 指定自己的 `panorama` 路径。 +- **热点**:在对应 scene 下配置 `hotSpots`(`pitch`、`yaw`、`type`、`text`、`sceneId` 等)。 + +可将 720 页面上的场景列表与热点信息手工抄写到 `config.json`,实现与线上类似的跳转效果。 + +--- + +## 四、技术说明 + +- **查看器**:[Pannellum](https://pannellum.org/)(轻量、开源、支持 equirectangular 全景)。 +- **图片格式**:支持 2:1 等距柱状投影(equirectangular)全景图;若 720 提供的是立方体六面图,可在 `config.json` 中设置 `type: "cubemap"` 并配置 `cubeMap` 六张图路径。 +- **版权**:仅建议对您拥有版权或已获授权的内容做本地化与离线使用。 + +--- + +## 五、常见问题 + +| 问题 | 处理 | +|------|------| +| 打开 index.html 空白或报错 | 必须通过 `http://localhost:端口` 访问,不要用 `file://`。 | +| 提示「全景图加载失败」 | 检查 `config.json` 里 `panorama` 或 `cubeMap` 路径是否正确,以及 `image/`(或 `panorama/`)下是否有对应文件。 | +| 自动脚本拿不到图 | 720 很多页面是前端请求接口再渲染,用「方式 B」在 Network 里找图片或接口即可。 | +| 想用 VR 眼镜 / 陀螺仪 | Pannellum 支持部分设备朝向;如需完整 VR,可考虑其全屏 + 设备 API 或其它 WebXR 方案。 | + +完成以上步骤后,该 720 链接对应的全景即可在本地完全离线查看与部署。 diff --git a/__pycache__/fetch_720yun.cpython-39.pyc b/__pycache__/fetch_720yun.cpython-39.pyc new file mode 100644 index 0000000..0b406f2 Binary files /dev/null and b/__pycache__/fetch_720yun.cpython-39.pyc differ diff --git a/build.js b/build.js new file mode 100644 index 0000000..c01cbb5 --- /dev/null +++ b/build.js @@ -0,0 +1,40 @@ +#!/usr/bin/env node +/** + * 将静态资源复制到 dist/,便于直接部署到任意静态主机。 + * 产出: dist/index.html, dist/config.json, dist/lib/, dist/image/ + */ +const fs = require('fs'); +const path = require('path'); + +const ROOT = path.resolve(__dirname); +const DIST = path.join(ROOT, 'dist'); + +function copyFile(src, dest) { + fs.mkdirSync(path.dirname(dest), { recursive: true }); + fs.copyFileSync(src, dest); +} + +function copyDir(srcDir, destDir) { + if (!fs.existsSync(srcDir)) return; + fs.mkdirSync(destDir, { recursive: true }); + for (const name of fs.readdirSync(srcDir)) { + const s = path.join(srcDir, name); + const d = path.join(destDir, name); + if (fs.statSync(s).isDirectory()) copyDir(s, d); + else copyFile(s, d); + } +} + +function main() { + if (fs.existsSync(DIST)) fs.rmSync(DIST, { recursive: true }); + fs.mkdirSync(DIST, { recursive: true }); + + copyFile(path.join(ROOT, 'index.html'), path.join(DIST, 'index.html')); + copyFile(path.join(ROOT, 'config.json'), path.join(DIST, 'config.json')); + copyDir(path.join(ROOT, 'lib'), path.join(DIST, 'lib')); + copyDir(path.join(ROOT, 'image'), path.join(DIST, 'image')); + + console.log('已构建到 dist/,可直接部署 dist 目录。'); +} + +main(); diff --git a/config.json b/config.json new file mode 100644 index 0000000..c903d77 --- /dev/null +++ b/config.json @@ -0,0 +1,21 @@ +{ + "type": "cubemap", + "cubeMap": [ + "image/mobile_f.jpg", + "image/mobile_r.jpg", + "image/mobile_b.jpg", + "image/mobile_l.jpg", + "image/mobile_u.jpg", + "image/mobile_d.jpg" + ], + "title": "广兴镇蔡家庵纪念 全景离线", + "autoLoad": true, + "showControls": true, + "hfov": 100, + "minHfov": 50, + "maxHfov": 120, + "authorName": "创作者:广兴镇蔡家11组原住民", + "authorUrl": "", + "viewCount": 0, + "watchingNow": 0 +} diff --git a/db.js b/db.js new file mode 100644 index 0000000..79442d8 --- /dev/null +++ b/db.js @@ -0,0 +1,137 @@ +/** + * SQLite 数据库:统计(观看、点赞、分享、在看)与留言(弹幕) + */ +const Database = require('better-sqlite3'); +const path = require('path'); + +const DB_PATH = path.join(__dirname, 'data', 'pano.db'); + +function getDb() { + const db = new Database(DB_PATH); + db.pragma('journal_mode = WAL'); + return db; +} + +function initDb() { + const fs = require('fs'); + const dir = path.join(__dirname, 'data'); + if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); + const db = getDb(); + db.exec(` + CREATE TABLE IF NOT EXISTS stats ( + id INTEGER PRIMARY KEY CHECK (id = 1), + view_count INTEGER NOT NULL DEFAULT 0, + like_count INTEGER NOT NULL DEFAULT 0, + share_count INTEGER NOT NULL DEFAULT 0, + watching_now INTEGER NOT NULL DEFAULT 0 + ); + INSERT OR IGNORE INTO stats (id, view_count, like_count, share_count, watching_now) VALUES (1, 0, 0, 0, 0); + + CREATE TABLE IF NOT EXISTS viewers ( + viewer_id TEXT PRIMARY KEY, + updated_at INTEGER NOT NULL + ); + + CREATE TABLE IF NOT EXISTS comments ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + content TEXT NOT NULL, + nickname TEXT, + created_at INTEGER NOT NULL + ); + `); + db.close(); +} + +function getStats() { + const db = getDb(); + const row = db.prepare('SELECT view_count, like_count, share_count, watching_now FROM stats WHERE id = 1').get(); + db.close(); + return { + viewCount: row.view_count, + likeCount: row.like_count, + shareCount: row.share_count, + watchingNow: row.watching_now, + }; +} + +function incView() { + const db = getDb(); + db.prepare('UPDATE stats SET view_count = view_count + 1 WHERE id = 1').run(); + const out = getStats(); + db.close(); + return out; +} + +function incLike() { + const db = getDb(); + db.prepare('UPDATE stats SET like_count = like_count + 1 WHERE id = 1').run(); + const row = db.prepare('SELECT like_count FROM stats WHERE id = 1').get(); + db.close(); + return { likeCount: row.like_count }; +} + +function incShare() { + const db = getDb(); + db.prepare('UPDATE stats SET share_count = share_count + 1 WHERE id = 1').run(); + const row = db.prepare('SELECT share_count FROM stats WHERE id = 1').get(); + db.close(); + return { shareCount: row.share_count }; +} + +function joinViewer(viewerId) { + const db = getDb(); + const now = Date.now(); + db.prepare('INSERT OR REPLACE INTO viewers (viewer_id, updated_at) VALUES (?, ?)').run(viewerId, now); + db.prepare('DELETE FROM viewers WHERE updated_at < ?').run(now - 120000); + const row = db.prepare('SELECT COUNT(*) as n FROM viewers').get(); + db.prepare('UPDATE stats SET watching_now = ? WHERE id = 1').run(row.n); + db.close(); + return row.n; +} + +function leaveViewer(viewerId) { + const db = getDb(); + db.prepare('DELETE FROM viewers WHERE viewer_id = ?').run(viewerId); + const row = db.prepare('SELECT COUNT(*) as n FROM viewers').get(); + db.prepare('UPDATE stats SET watching_now = ? WHERE id = 1').run(row.n); + db.close(); + return row.n; +} + +function getComments(limit = 100) { + const db = getDb(); + const rows = db.prepare( + 'SELECT id, content, nickname, created_at FROM comments ORDER BY id DESC LIMIT ?' + ).all(limit); + db.close(); + return rows.reverse().map((r) => ({ + id: r.id, + content: r.content, + nickname: r.nickname || '游客', + createdAt: r.created_at, + })); +} + +function addComment(content, nickname) { + const db = getDb(); + const now = Date.now(); + const r = db.prepare('INSERT INTO comments (content, nickname, created_at) VALUES (?, ?, ?)').run( + String(content).trim().slice(0, 200) || '(空)', + nickname ? String(nickname).trim().slice(0, 32) : null, + now + ); + db.close(); + return { id: r.lastInsertRowid, content: content.trim().slice(0, 200), nickname: nickname || '游客', createdAt: now }; +} + +module.exports = { + initDb, + getStats, + incView, + incLike, + incShare, + joinViewer, + leaveViewer, + getComments, + addComment, +}; diff --git a/docs/720yun_资源与鉴权说明.md b/docs/720yun_资源与鉴权说明.md new file mode 100644 index 0000000..1d37356 --- /dev/null +++ b/docs/720yun_资源与鉴权说明.md @@ -0,0 +1,49 @@ +# 720yun 原项目资源与鉴权说明 + +基于对 `text.md`(720yun 页面保存的 HTML/脚本)的检查,说明**是否有加密措施**、**为什么脚本读取与浏览器不一致**,以及**底面图的实际地址**。 + +--- + +## 一、原项目是否有加密/鉴权措施? + +文档中**没有**对图片内容做加密(如 AES、自定义编解码)。资源 URL 为明文路径,不携带 token 或签名。 + +但存在以下**访问控制**,会影响脚本直接拉取或离线使用: + +| 措施 | 说明 | +|------|------| +| **Referer 校验** | 页面含 ``,请求会带来源页。CDN(如 resource-t.720static.com)很可能只允许来自 `*.720yun.com` 的 Referer,否则关闭连接或返回异常。用 Python/脚本直接请求时未带该 Referer,易出现连接被关(如 EOF)。 | +| **场景 JSON 不直出** | 场景配置在 `window.json="json/4ca3fae5e7x/.../3.json"`,由前端在浏览器里请求。该接口可能校验 Cookie/Origin/Referer,脚本直接 GET 常返回 403/404 或空,无法拿到完整 cube 路径列表。 | +| **过期与权限字段** | `window.data` 中有 `"expired":1`、`"urlExpiredDate":null`、`"privilege":1`。若服务端按 `expired` 或 `urlExpiredDate` 做时效控制,过期后可能拒绝部分接口或资源。 | + +**结论**:没有对图片做“加密”,但有 **Referer/来源校验** 和可能的 **时效/权限** 控制,所以用脚本从站外直接拉取会失败或拿不全;在浏览器里从 720yun 页面打开时,由同源请求带正确 Referer,才能正常加载。 + +### 为什么 Chrome 能访问、脚本“读取”就不正确? + +- 浏览器请求 720 的 CDN 时会自动带上 **Referer: https://www.720yun.com/**(以及完整 User-Agent)。CDN 只认这类请求,返回 200 和正确图片。 +- 脚本若用默认 `urllib` 或只带简单 User-Agent、**不带 Referer**,CDN 会拒绝或关闭连接,你看到的就是“读取不正确”、EOF、403 等。 +- **解决办法**:脚本里请求 720static.com 的 URL 时,必须加上与浏览器一致的请求头,例如: + - `Referer: https://www.720yun.com/` + - `User-Agent: Mozilla/5.0 (Macintosh; ...) Chrome/120.0.0.0 Safari/537.36` +- 本仓库已在 `fetch_720yun.py` 的 `download_file` 和 `parse_720yun_doc.py` 的下载逻辑里使用上述头,用 `--download` 拉取时与浏览器行为一致。 + +--- + +## 二、底面图(mobile_d)的实际地址与 CDN 域名 + +原项目**有底图**,地址为(与浏览器一致): + +- **底面**:`https://ssl-panoimg130.720static.com/resource/prod/4ca3fae5e7x/d22jkguytw6/59446768/imgs/mobile_d.jpg` + +之前解析脚本里用的资源域是 **resource-t.720static.com**,而真实全景图 CDN 是 **ssl-panoimg130.720static.com**。用错域名时,脚本请求会失败或拿不到正确数据;在浏览器里因为页面请求的是正确域名,所以“在浏览器里是正确的数据”。 + +- 解析脚本已改为使用 **ssl-panoimg130.720static.com** 作为资源 base,生成的 6 面 + 缩略图 URL 与浏览器一致。 +- 下载时需同时带 **Referer: https://www.720yun.com/** 和浏览器 User-Agent(见上一节),否则 CDN 仍可能拒绝,导致“读取”与浏览器不一致。 + +--- + +## 三、在本项目里怎么用(含底面) + +- 解析出的 6 面 URL 已使用正确 CDN(ssl-panoimg130.720static.com),且含底面 mobile_d.jpg。 +- 在项目根目录执行 **`python3 parse_720yun_doc.py --download`**,会用与浏览器一致的 Referer/User-Agent 把 6 面 + 缩略图下载到 `image/`,再在页面上选「选择六面体(6张)」按顺序选 image 下 6 张即可。 +- 若仍只有 5 张(没有 d),可用一张纯黑或占位图作为第 6 张,或在 config 的 cubemap 里第 6 个用占位图路径。 diff --git a/fetch_720yun.py b/fetch_720yun.py new file mode 100644 index 0000000..a3d70c0 --- /dev/null +++ b/fetch_720yun.py @@ -0,0 +1,255 @@ +#!/usr/bin/env python3 +""" +从 720yun 页面抓取全景资源并本地化。 +用法: python3 fetch_720yun.py "https://www.720yun.com/vr/c8525usOunr" +若页面由 JS 动态加载,请使用「手动获取」方式(见 README)。 +""" +import re +import sys +import json +import urllib.request +import urllib.error +from pathlib import Path + +def fetch_html(url): + req = urllib.request.Request(url, headers={ + 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36' + }) + with urllib.request.urlopen(req, timeout=15) as r: + return r.read().decode('utf-8', errors='replace') + +def _extract_balanced_json(html, start_marker, open_char='{', close_char='}'): + """从 html 中查找 start_marker 后紧跟的完整 JSON(匹配括号)。""" + results = [] + i = 0 + if open_char == '{': + other_open, other_close = '[', ']' + else: + other_open, other_close = '{', '}' + while True: + pos = html.find(start_marker, i) + if pos < 0: + break + start = pos + len(start_marker) + # 跳过空白与等号 + while start < len(html) and html[start] in ' \t\n=': + start += 1 + if start >= len(html): + i = pos + 1 + continue + if html[start] == open_char: + depth = 0 + in_string = None + escape = False + j = start + while j < len(html): + c = html[j] + if escape: + escape = False + j += 1 + continue + if c == '\\' and in_string: + escape = True + j += 1 + continue + if in_string: + if c == in_string: + in_string = None + j += 1 + continue + if c in '"\'': + in_string = c + j += 1 + continue + if c == open_char: + depth += 1 + elif c == close_char: + depth -= 1 + if depth == 0: + results.append(html[start:j + 1]) + break + elif c == other_open: + depth += 1 + elif c == other_close: + depth -= 1 + j += 1 + elif html[start] == other_open: + depth = 0 + in_string = None + escape = False + j = start + while j < len(html): + c = html[j] + if escape: + escape = False + j += 1 + continue + if c == '\\' and in_string: + escape = True + j += 1 + continue + if in_string: + if c == in_string: + in_string = None + j += 1 + continue + if c in '"\'': + in_string = c + j += 1 + continue + if c == other_open: + depth += 1 + elif c == other_close: + depth -= 1 + if depth == 0: + results.append(html[start:j + 1]) + break + elif c == open_char: + depth += 1 + elif c == close_char: + depth -= 1 + j += 1 + i = pos + 1 + return results + + +def find_json_assignments(html): + """查找页面中常见的 __INITIAL_STATE__、window.__DATA__ 等 JSON 赋值(支持嵌套)。""" + markers = [ + 'window.__INITIAL_STATE__', + '__INITIAL_STATE__', + 'window.__DATA__', + 'window.__NUXT_DATA__', + ] + results = [] + for marker in markers: + results.extend(_extract_balanced_json(html, marker, '{', '}')) + # 也尝试匹配 "panorama":"url" 或 "scenes":[...] 的简单模式 + for m in re.finditer(r'"panorama"\s*:\s*"([^"]+)"', html): + results.append(m.group(1)) + return results + +def find_image_urls(html): + """从 HTML 中提取可能是全景图的 URL(720yun CDN 等)。""" + # 常见 720 云图片域名 + url_pattern = re.compile( + r'https?://[^\s"\'<>]+?\.(?:720yun\.com|qpic\.cn|gtimg\.com)[^\s"\'<>]*\.(?:jpg|jpeg|png|webp)', + re.I + ) + urls = list(set(url_pattern.findall(html))) + # 也匹配任意包含 panorama / scene / photo 的图片 URL + alt_pattern = re.compile( + r'https?://[^\s"\'<>]+?/(?:panorama|scene|photo|pano|vr)[^\s"\'<>]*\.(?:jpg|jpeg|png|webp)', + re.I + ) + for u in alt_pattern.findall(html): + if u not in urls: + urls.append(u) + return urls + +# 720yun CDN 会校验 Referer,脚本请求需与浏览器一致才能拿到正确数据 +def _browser_headers(url=''): + h = { + 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Accept': 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.9', + } + if '720static.com' in url or '720yun.com' in url: + h['Referer'] = 'https://www.720yun.com/' + return h + + +def download_file(url, dest_path): + req = urllib.request.Request(url, headers=_browser_headers(url)) + with urllib.request.urlopen(req, timeout=30) as r: + dest_path.write_bytes(r.read()) + +def main(): + if len(sys.argv) < 2: + print('用法: python3 fetch_720yun.py <720yun页面URL>') + print('例: python3 fetch_720yun.py "https://www.720yun.com/vr/c8525usOunr"') + sys.exit(1) + url = sys.argv[1].strip() + base = Path(__file__).resolve().parent + panorama_dir = base / 'panorama' + panorama_dir.mkdir(exist_ok=True) + config_path = base / 'config.json' + + print('正在请求页面...') + try: + html = fetch_html(url) + except Exception as e: + print('请求失败:', e) + print('请使用 README 中的「手动获取」方式在浏览器中抓取资源。') + sys.exit(1) + + image_urls = find_image_urls(html) + json_candidates = find_json_assignments(html) + + # 尝试从 JSON 中解析 panorama 或 scenes + for raw in json_candidates: + try: + if raw.startswith('http'): + image_urls.append(raw) + continue + if raw.startswith('{'): + data = json.loads(raw) + elif raw.startswith('['): + data = json.loads(raw) + if data and isinstance(data[0], dict) and 'url' in data[0]: + image_urls.extend([s.get('url') for s in data if s.get('url')]) + continue + else: + continue + # 递归查找 url / panorama / image 字段 + def collect_urls(obj, out): + if isinstance(obj, dict): + for k, v in obj.items(): + if k in ('url', 'panorama', 'image', 'src', 'pic') and isinstance(v, str) and v.startswith('http'): + out.append(v) + else: + collect_urls(v, out) + elif isinstance(obj, list): + for x in obj: + collect_urls(x, out) + collect_urls(data, image_urls) + except (json.JSONDecodeError, TypeError): + pass + + image_urls = list(dict.fromkeys([u for u in image_urls if u and u.startswith('http')])) + + if not image_urls: + print('未在页面 HTML 中发现全景图 URL(页面可能由 JavaScript 动态加载)。') + print('请按 README 使用浏览器开发者工具手动获取:') + print(' 1. 打开该 720yun 链接') + print(' 2. F12 -> Network -> 刷新 -> 筛选 Img 或 XHR') + print(' 3. 找到全景图或 scene 接口返回的图片 URL,下载到 panorama/ 并命名为 panorama.jpg') + print(' 4. 确保 config.json 中 panorama 为 "panorama/panorama.jpg"') + sys.exit(0) + + print('发现可能的全景图 URL:', len(image_urls)) + local_path = panorama_dir / 'panorama.jpg' + try: + first = image_urls[0] + print('正在下载:', first[:80], '...') + download_file(first, local_path) + print('已保存到:', local_path) + except Exception as e: + print('下载失败:', e) + print('请手动将上面列出的任一 URL 在浏览器中打开并另存为 panorama/panorama.jpg') + sys.exit(1) + + # 确保 config 指向本地 + if config_path.exists(): + with open(config_path, 'r', encoding='utf-8') as f: + config = json.load(f) + else: + config = {} + config['panorama'] = 'panorama/panorama.jpg' + config['type'] = config.get('type', 'equirectangular') + config['title'] = config.get('title', '本地全景') + with open(config_path, 'w', encoding='utf-8') as f: + json.dump(config, f, ensure_ascii=False, indent=2) + print('已更新 config.json。运行本地服务器后打开 index.html 即可离线查看。') + +if __name__ == '__main__': + main() diff --git a/image/mobile_b.jpg b/image/mobile_b.jpg new file mode 100644 index 0000000..73a121e Binary files /dev/null and b/image/mobile_b.jpg differ diff --git a/image/mobile_d.jpg b/image/mobile_d.jpg new file mode 100644 index 0000000..c2eb2f6 Binary files /dev/null and b/image/mobile_d.jpg differ diff --git a/image/mobile_f.jpg b/image/mobile_f.jpg new file mode 100644 index 0000000..8ad5234 Binary files /dev/null and b/image/mobile_f.jpg differ diff --git a/image/mobile_l.jpg b/image/mobile_l.jpg new file mode 100644 index 0000000..d20d498 Binary files /dev/null and b/image/mobile_l.jpg differ diff --git a/image/mobile_r.jpg b/image/mobile_r.jpg new file mode 100644 index 0000000..8a147ad Binary files /dev/null and b/image/mobile_r.jpg differ diff --git a/image/mobile_u.jpg b/image/mobile_u.jpg new file mode 100644 index 0000000..1bcf756 Binary files /dev/null and b/image/mobile_u.jpg differ diff --git a/image/preview.jpg b/image/preview.jpg new file mode 100644 index 0000000..787f4e1 Binary files /dev/null and b/image/preview.jpg differ diff --git a/image/thumb.jpg b/image/thumb.jpg new file mode 100644 index 0000000..21b5d06 Binary files /dev/null and b/image/thumb.jpg differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..1e69df7 --- /dev/null +++ b/index.html @@ -0,0 +1,456 @@ + + + + + + 全景查看 + + + + +
+
+
+
+
+
+
+ +
+ +
+
+ +
+ 0 人在看 + 共 0 次播放 +
+
+
+ +
+
+ +
+
+
+
简介
+
分享 0
+
0
+
留言
+
+
+
+
+
+
+ + + + + + + diff --git a/lib/pannellum.css b/lib/pannellum.css new file mode 100644 index 0000000..3528cd4 --- /dev/null +++ b/lib/pannellum.css @@ -0,0 +1,2 @@ +/* Pannellum 2.5.7, https://github.com/mpetroff/pannellum */ +.pnlm-container{margin:0;padding:0;overflow:hidden;position:relative;cursor:default;width:100%;height:100%;font-family:Helvetica,"Nimbus Sans L","Liberation Sans",Arial,sans-serif;background:#f4f4f4 url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2267%22%20height%3D%22100%22%20viewBox%3D%220%200%2067%20100%22%3E%0A%3Cpath%20stroke%3D%22%23ccc%22%20fill%3D%22none%22%20d%3D%22M33.5%2C50%2C0%2C63%2C33.5%2C75%2C67%2C63%2C33.5%2C50m-33.5-50%2C67%2C25m-0.5%2C0%2C0%2C75m-66.5-75%2C67-25m-33.5%2C75%2C0%2C25m0-100%2C0%2C50%22%2F%3E%0A%3C%2Fsvg%3E%0A') repeat;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-o-user-select:none;-ms-user-select:none;user-select:none;outline:0;line-height:1.4;contain:content}.pnlm-container *{box-sizing:content-box}.pnlm-ui{position:absolute;width:100%;height:100%;z-index:1}.pnlm-grab{cursor:grab;cursor:url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2226%22%20width%3D%2226%22%3E%0A%3Cpath%20stroke%3D%22%23000%22%20stroke-width%3D%221px%22%20fill%3D%22%23fff%22%20d%3D%22m15.3%2020.5s6.38-6.73%204.64-8.24-3.47%201.01-3.47%201.01%203.61-5.72%201.41-6.49c-2.2-0.769-3.33%204.36-3.33%204.36s0.873-5.76-1.06-5.76-1.58%205.39-1.58%205.39-0.574-4.59-2.18-4.12c-1.61%200.468-0.572%205.51-0.572%205.51s-1.58-4.89-2.93-3.79c-1.35%201.11%200.258%205.25%200.572%206.62%200.836%202.43%202.03%202.94%202.17%205.55%22%2F%3E%0A%3C%2Fsvg%3E%0A') 12 8,default}.pnlm-grabbing{cursor:grabbing;cursor:url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2226%22%20width%3D%2226%22%3E%0A%3Cpath%20stroke%3D%22%23000%22%20stroke-width%3D%221px%22%20fill%3D%22%23fff%22%20d%3D%22m15.3%2020.5s5.07-5.29%203.77-6.74c-1.31-1.45-2.53%200.14-2.53%200.14s2.74-3.29%200.535-4.06c-2.2-0.769-2.52%201.3-2.52%201.3s0.81-2.13-1.12-2.13-1.52%201.77-1.52%201.77-0.261-1.59-1.87-1.12c-1.61%200.468-0.874%202.17-0.874%202.17s-0.651-1.55-2-0.445c-1.35%201.11-0.68%202.25-0.365%203.62%200.836%202.43%202.03%202.94%202.17%205.55%22%2F%3E%0A%3C%2Fsvg%3E%0A') 12 8,default}.pnlm-sprite{background-image:url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2226%22%20height%3D%22208%22%3E%0A%3Ccircle%20fill-opacity%3D%22.78%22%20cy%3D%22117%22%20cx%3D%2213%22%20r%3D%2211%22%20fill%3D%22%23fff%22%2F%3E%0A%3Ccircle%20fill-opacity%3D%22.78%22%20cy%3D%22143%22%20cx%3D%2213%22%20r%3D%2211%22%20fill%3D%22%23fff%22%2F%3E%0A%3Ccircle%20cy%3D%22169%22%20cx%3D%2213%22%20r%3D%227%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%222%22%2F%3E%0A%3Ccircle%20cy%3D%22195%22%20cx%3D%2213%22%20r%3D%227%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%222%22%2F%3E%0A%3Ccircle%20cx%3D%2213%22%20cy%3D%22195%22%20r%3D%222.5%22%2F%3E%0A%3Cpath%20d%3D%22m5%2083v6h2v-4h4v-2zm10%200v2h4v4h2v-6zm-5%205v6h6v-6zm-5%205v6h6v-2h-4v-4zm14%200v4h-4v2h6v-6z%22%2F%3E%0A%3Cpath%20d%3D%22m13%20110a7%207%200%200%200%20-7%207%207%207%200%200%200%207%207%207%207%200%200%200%207%20-7%207%207%200%200%200%20-7%20-7zm-1%203h2v2h-2zm0%203h2v5h-2z%22%2F%3E%0A%3Cpath%20d%3D%22m5%2057v6h2v-4h4v-2zm10%200v2h4v4h2v-6zm-10%2010v6h6v-2h-4v-4zm14%200v4h-4v2h6v-6z%22%2F%3E%0A%3Cpath%20d%3D%22m17%2038v2h-8v-2z%22%2F%3E%0A%3Cpath%20d%3D%22m12%209v3h-3v2h3v3h2v-3h3v-2h-3v-3z%22%2F%3E%0A%3Cpath%20d%3D%22m13%20136-6.125%206.125h4.375v7.875h3.5v-7.875h4.375z%22%2F%3E%0A%3Cpath%20d%3D%22m10.428%20173.33v-5.77l5-2.89v5.77zm1-1.73%203-1.73-3.001-1.74z%22%2F%3E%0A%3C%2Fsvg%3E%0A')}.pnlm-container:-moz-full-screen{height:100%!important;width:100%!important;position:static!important}.pnlm-container:-webkit-full-screen{height:100%!important;width:100%!important;position:static!important}.pnlm-container:-ms-fullscreen{height:100%!important;width:100%!important;position:static!important}.pnlm-container:fullscreen{height:100%!important;width:100%!important;position:static!important}.pnlm-render-container{cursor:inherit;position:absolute;height:100%;width:100%}.pnlm-controls{margin-top:4px;background-color:#fff;border:1px solid #999;border-color:rgba(0,0,0,0.4);border-radius:3px;cursor:pointer;z-index:2;-webkit-transform:translateZ(9999px);transform:translateZ(9999px)}.pnlm-control:hover{background-color:#f8f8f8}.pnlm-controls-container{position:absolute;top:0;left:4px;z-index:1}.pnlm-zoom-controls{width:26px;height:52px}.pnlm-zoom-in{width:100%;height:50%;position:absolute;top:0;border-radius:3px 3px 0 0}.pnlm-zoom-out{width:100%;height:50%;position:absolute;bottom:0;background-position:0 -26px;border-top:1px solid #ddd;border-top-color:rgba(0,0,0,0.10);border-radius:0 0 3px 3px}.pnlm-fullscreen-toggle-button,.pnlm-orientation-button,.pnlm-hot-spot-debug-indicator{width:26px;height:26px}.pnlm-hot-spot-debug-indicator{position:absolute;top:50%;left:50%;width:26px;height:26px;margin:-13px 0 0 -13px;background-color:rgba(255,255,255,0.5);border-radius:13px;display:none}.pnlm-orientation-button-inactive{background-position:0 -156px}.pnlm-orientation-button-active{background-position:0 -182px}.pnlm-fullscreen-toggle-button-inactive{background-position:0 -52px}.pnlm-fullscreen-toggle-button-active{background-position:0 -78px}.pnlm-panorama-info{position:absolute;bottom:4px;background-color:rgba(0,0,0,0.7);border-radius:0 3px 3px 0;padding-right:10px;color:#fff;text-align:left;display:none;z-index:2;-webkit-transform:translateZ(9999px);transform:translateZ(9999px)}.pnlm-title-box{position:relative;font-size:20px;display:table;padding-left:5px;margin-bottom:3px}.pnlm-author-box{position:relative;font-size:12px;display:table;padding-left:5px}.pnlm-load-box{position:absolute;top:50%;left:50%;width:200px;height:150px;margin:-75px 0 0 -100px;background-color:rgba(0,0,0,0.7);border-radius:3px;text-align:center;font-size:20px;display:none;color:#fff}.pnlm-load-box p{margin:20px 0}.pnlm-lbox{position:absolute;top:50%;left:50%;width:20px;height:20px;margin:-10px 0 0 -10px;display:none}.pnlm-loading{animation-duration:1.5s;-webkit-animation-duration:1.5s;animation-name:pnlm-mv;-webkit-animation-name:pnlm-mv;animation-iteration-count:infinite;-webkit-animation-iteration-count:infinite;animation-timing-function:linear;-webkit-animation-timing-function:linear;height:10px;width:10px;background-color:#fff;position:relative}@keyframes pnlm-mv{from{left:0;top:0}25%{left:10px;top:0}50%{left:10px;top:10px}75%{left:0;top:10px}to{left:0;top:0}}@-webkit-keyframes pnlm-mv{from{left:0;top:0}25%{left:10px;top:0}50%{left:10px;top:10px}75%{left:0;top:10px}to{left:0;top:0}}.pnlm-load-button{position:absolute;top:50%;left:50%;width:200px;height:100px;margin:-50px 0 0 -100px;background-color:rgba(0,0,0,.7);border-radius:3px;text-align:center;font-size:20px;display:table;color:#fff;cursor:pointer}.pnlm-load-button:hover{background-color:rgba(0,0,0,.8)}.pnlm-load-button p{display:table-cell;vertical-align:middle}.pnlm-info-box{font-size:15px;position:absolute;top:50%;left:50%;width:200px;height:150px;margin:-75px 0 0 -100px;background-color:#000;border-radius:3px;display:table;text-align:center;color:#fff;table-layout:fixed}.pnlm-info-box a,.pnlm-author-box a{color:#fff;word-wrap:break-word;overflow-wrap:break-word}.pnlm-info-box p{display:table-cell;vertical-align:middle;padding:0 5px 0 5px}.pnlm-error-msg{display:none}.pnlm-about-msg{font-size:11px;line-height:11px;color:#fff;padding:5px 8px 5px 8px;background:rgba(0,0,0,0.7);border-radius:3px;position:absolute;top:50px;left:50px;display:none;opacity:0;-moz-transition:opacity .3s ease-in-out;-webkit-transition:opacity .3s ease-in-out;-o-transition:opacity .3s ease-in-out;-ms-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out;z-index:1}.pnlm-about-msg a:link,.pnlm-about-msg a:visited{color:#fff}.pnlm-about-msg a:hover,.pnlm-about-msg a:active{color:#eee}.pnlm-hotspot-base{position:absolute;visibility:hidden;cursor:default;vertical-align:middle;top:0;z-index:1}.pnlm-hotspot{height:26px;width:26px;border-radius:13px}.pnlm-hotspot:hover{background-color:rgba(255,255,255,0.2)}.pnlm-hotspot.pnlm-info{background-position:0 -104px}.pnlm-hotspot.pnlm-scene{background-position:0 -130px}div.pnlm-tooltip span{visibility:hidden;position:absolute;border-radius:3px;background-color:rgba(0,0,0,0.7);color:#fff;text-align:center;max-width:200px;padding:5px 10px;margin-left:-220px;cursor:default}div.pnlm-tooltip:hover span{visibility:visible}div.pnlm-tooltip:hover span:after{content:'';position:absolute;width:0;height:0;border-width:10px;border-style:solid;border-color:rgba(0,0,0,0.7) transparent transparent transparent;bottom:-20px;left:-10px;margin:0 50%}.pnlm-compass{position:absolute;width:50px;height:50px;right:4px;bottom:4px;border-radius:25px;background-image:url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2250%22%20width%3D%2250%22%3E%0A%3Cpath%20d%3D%22m24.5078%206-3.2578%2018h7.5l-3.25781-18h-0.984376zm-3.2578%2020%203.2578%2018h0.9844l3.2578-18h-7.5zm1.19531%200.9941h5.10938l-2.5547%2014.1075-2.5547-14.1075z%22%2F%3E%0A%3C%2Fsvg%3E%0A');cursor:default;display:none}.pnlm-world{position:absolute;left:50%;top:50%}.pnlm-face{position:absolute;-webkit-transform-origin:0 0;transform-origin:0 0}.pnlm-dragfix,.pnlm-preview-img{position:absolute;height:100%;width:100%}.pnlm-preview-img{background-size:cover;background-position:center}.pnlm-lbar{width:150px;margin:0 auto;border:#fff 1px solid;height:6px}.pnlm-lbar-fill{background:#fff;height:100%;width:0}.pnlm-lmsg{font-size:12px}.pnlm-fade-img{position:absolute;top:0;left:0}.pnlm-pointer{cursor:pointer} \ No newline at end of file diff --git a/lib/pannellum.js b/lib/pannellum.js new file mode 100644 index 0000000..415c1b6 --- /dev/null +++ b/lib/pannellum.js @@ -0,0 +1,110 @@ +// Pannellum 2.5.7, https://github.com/mpetroff/pannellum +window.libpannellum=function(E,g,p){function Ba(K){function ja(a,d){return 1==a.level&&1!=d.level?-1:1==d.level&&1!=a.level?1:d.timestamp-a.timestamp}function Q(a,d){return a.level!=d.level?a.level-d.level:a.diff-d.diff}function ka(a,d,c,g,k,h){this.vertices=a;this.side=d;this.level=c;this.x=g;this.y=k;this.path=h.replace("%s",d).replace("%l",c).replace("%x",g).replace("%y",k)}function Ja(a,d,g,p,k){var h;var e=d.vertices;h=la(a,e.slice(0,3));var u=la(a,e.slice(3,6)),x=la(a,e.slice(6,9)),e=la(a,e.slice(9, +12)),t=h[0]+u[0]+x[0]+e[0];-4==t||4==t?h=!1:(t=h[1]+u[1]+x[1]+e[1],h=-4==t||4==t?!1:4!=h[2]+u[2]+x[2]+e[2]);if(h){h=d.vertices;u=h[0]+h[3]+h[6]+h[9];x=h[1]+h[4]+h[7]+h[10];e=h[2]+h[5]+h[8]+h[11];t=Math.sqrt(u*u+x*x+e*e);e=Math.asin(e/t);u=Math.atan2(x,u)-p;u+=u>Math.PI?-2*Math.PI:u<-Math.PI?2*Math.PI:0;u=Math.abs(u);d.diff=Math.acos(Math.sin(g)*Math.sin(e)+Math.cos(g)*Math.cos(e)*Math.cos(u));u=!1;for(x=0;xg&&(h[0]=-1);1k&&(h[1]=-1);1c||1t;t++)0==m[t].width&&(m[t]=a)}}d===p&&(d="equirectangular");if("equirectangular"!=d&&"cubemap"!=d&&"multires"!=d)throw console.log("Error: invalid image type specified!"), +{type:"config error"};z=d;m=L;G=Ca;va=u||{};if(c){U&&(a.detachShader(c,U),a.deleteShader(U));V&&(a.detachShader(c,V),a.deleteShader(V));a.bindBuffer(a.ARRAY_BUFFER,null);a.bindBuffer(a.ELEMENT_ARRAY_BUFFER,null);c.texture&&a.deleteTexture(c.texture);if(c.nodeCache)for(L=0;Lt;t++)0e;e++)c[4*(b+a.width)+e]=c[4*(b+2*a.width)+e],c[4*(b+a.width*(a.height-2))+e]=c[4*(b+a.width*(a.height-3))+e];for(b=2;be;e++)c[4*(b*a.width+1)+e]=c[4*(b*a.width+2)+e],c[4*((b+1)*a.width-2)+e]=c[4*((b+1)*a.width-3)+e];for(e=0;4>e;e++)c[4*(a.width+1)+e]=c[4*(2*a.width+2)+e],c[4*(2*a.width-2)+e]=c[4*(3*a.width-3)+e],c[4*(a.width*(a.height-2)+1)+e]=c[4* +(a.width*(a.height-3)+2)+e],c[4*(a.width*(a.height-1)-2)+e]=c[4*(a.width*(a.height-2)-3)+e];for(b=1;be;e++)c[4*b+e]=c[4*(b+a.width)+e],c[4*(b+a.width*(a.height-1))+e]=c[4*(b+a.width*(a.height-2))+e];for(b=1;be;e++)c[b*a.width*4+e]=c[4*(b*a.width+1)+e],c[4*((b+1)*a.width-1)+e]=c[4*((b+1)*a.width-2)+e];for(e=0;4>e;e++)c[e]=c[4*(a.width+1)+e],c[4*(a.width-1)+e]=c[4*(2*a.width-2)+e],c[a.width*(a.height-1)*4+e]=c[4*(a.width*(a.height-2)+1)+e],c[4*(a.width* +a.height-1)+e]=c[4*(a.width*(a.height-1)-2)+e];d.putImageData(h,0,0);D.call(this)};var D=function(){0t;t++)h=new Image,h.crossOrigin=va.crossOrigin?va.crossOrigin:"anonymous",h.side=t,h.onload=k,h.onerror=D,h.src="multires"==z?I.replace("%s",Q[t])+"."+m.extension:m[t].src;x($)}else{if(!a)throw console.log("Error: no WebGL support detected!"), +{type:"no webgl"};"cubemap"==z&&x(y);m.fullpath=m.basePath?m.basePath+m.path:m.path;m.invTileResolution=1/m.tileResolution;L=ta();wa=[];for(t=0;6>t;t++)wa[t]=L.slice(12*t,12*t+12),L=ta();L=0;if("equirectangular"==z){if(L=a.getParameter(a.MAX_TEXTURE_SIZE),Math.max(m.width/2,m.height)>L)throw console.log("Error: The image is too big; it's "+m.width+"px wide, but this device's maximum supported size is "+2*L+"px."),{type:"webgl size error",width:m.width,maxWidth:2*L};}else if("cubemap"==z&&y>a.getParameter(a.MAX_CUBE_MAP_TEXTURE_SIZE))throw console.log("Error: The image is too big; it's "+ +y+"px wide, but this device's maximum supported size is "+L+"px."),{type:"webgl size error",width:y,maxWidth:L};u===p||u.horizonPitch===p&&u.horizonRoll===p||(ga=[u.horizonPitch==p?0:u.horizonPitch,u.horizonRoll==p?0:u.horizonRoll]);y=a.TEXTURE_2D;a.viewport(0,0,a.drawingBufferWidth,a.drawingBufferHeight);a.getShaderPrecisionFormat&&(d=a.getShaderPrecisionFormat(a.FRAGMENT_SHADER,a.HIGH_FLOAT))&&1>d.precision&&(oa=oa.replace("highp","mediump"));U=a.createShader(a.VERTEX_SHADER);d=s;"multires"==z&& +(d=l);a.shaderSource(U,d);a.compileShader(U);V=a.createShader(a.FRAGMENT_SHADER);d=pa;"cubemap"==z?(y=a.TEXTURE_CUBE_MAP,d=qa):"multires"==z&&(d=bb);a.shaderSource(V,d);a.compileShader(V);c=a.createProgram();a.attachShader(c,U);a.attachShader(c,V);a.linkProgram(c);a.getShaderParameter(U,a.COMPILE_STATUS)||console.log(a.getShaderInfoLog(U));a.getShaderParameter(V,a.COMPILE_STATUS)||console.log(a.getShaderInfoLog(V));a.getProgramParameter(c,a.LINK_STATUS)||console.log(a.getProgramInfoLog(c));a.useProgram(c); +c.drawInProgress=!1;d=u.backgroundColor?u.backgroundColor:[0,0,0];a.clearColor(d[0],d[1],d[2],1);a.clear(a.COLOR_BUFFER_BIT);c.texCoordLocation=a.getAttribLocation(c,"a_texCoord");a.enableVertexAttribArray(c.texCoordLocation);"multires"!=z?(ca||(ca=a.createBuffer()),a.bindBuffer(a.ARRAY_BUFFER,ca),a.bufferData(a.ARRAY_BUFFER,new Float32Array([-1,1,1,1,1,-1,-1,1,1,-1,-1,-1]),a.STATIC_DRAW),a.vertexAttribPointer(c.texCoordLocation,2,a.FLOAT,!1,0,0),c.aspectRatio=a.getUniformLocation(c,"u_aspectRatio"), +a.uniform1f(c.aspectRatio,a.drawingBufferWidth/a.drawingBufferHeight),c.psi=a.getUniformLocation(c,"u_psi"),c.theta=a.getUniformLocation(c,"u_theta"),c.f=a.getUniformLocation(c,"u_f"),c.h=a.getUniformLocation(c,"u_h"),c.v=a.getUniformLocation(c,"u_v"),c.vo=a.getUniformLocation(c,"u_vo"),c.rot=a.getUniformLocation(c,"u_rot"),a.uniform1f(c.h,I/(2*Math.PI)),a.uniform1f(c.v,k/Math.PI),a.uniform1f(c.vo,h/Math.PI*2),"equirectangular"==z&&(c.backgroundColor=a.getUniformLocation(c,"u_backgroundColor"),a.uniform4fv(c.backgroundColor, +d.concat([1]))),c.texture=a.createTexture(),a.bindTexture(y,c.texture),"cubemap"==z?(a.texImage2D(a.TEXTURE_CUBE_MAP_POSITIVE_X,0,a.RGB,a.RGB,a.UNSIGNED_BYTE,m[1]),a.texImage2D(a.TEXTURE_CUBE_MAP_NEGATIVE_X,0,a.RGB,a.RGB,a.UNSIGNED_BYTE,m[3]),a.texImage2D(a.TEXTURE_CUBE_MAP_POSITIVE_Y,0,a.RGB,a.RGB,a.UNSIGNED_BYTE,m[4]),a.texImage2D(a.TEXTURE_CUBE_MAP_NEGATIVE_Y,0,a.RGB,a.RGB,a.UNSIGNED_BYTE,m[5]),a.texImage2D(a.TEXTURE_CUBE_MAP_POSITIVE_Z,0,a.RGB,a.RGB,a.UNSIGNED_BYTE,m[0]),a.texImage2D(a.TEXTURE_CUBE_MAP_NEGATIVE_Z, +0,a.RGB,a.RGB,a.UNSIGNED_BYTE,m[2])):m.width<=L?(a.uniform1i(a.getUniformLocation(c,"u_splitImage"),0),a.texImage2D(y,0,a.RGB,a.RGB,a.UNSIGNED_BYTE,m)):(a.uniform1i(a.getUniformLocation(c,"u_splitImage"),1),I=g.createElement("canvas"),I.width=m.width/2,I.height=m.height,I=I.getContext("2d"),I.drawImage(m,0,0),k=I.getImageData(0,0,m.width/2,m.height),a.texImage2D(y,0,a.RGB,a.RGB,a.UNSIGNED_BYTE,k),c.texture2=a.createTexture(),a.activeTexture(a.TEXTURE1),a.bindTexture(y,c.texture2),a.uniform1i(a.getUniformLocation(c, +"u_image1"),1),I.drawImage(m,-m.width/2,0),k=I.getImageData(0,0,m.width/2,m.height),a.texImage2D(y,0,a.RGB,a.RGB,a.UNSIGNED_BYTE,k),a.texParameteri(y,a.TEXTURE_WRAP_S,a.CLAMP_TO_EDGE),a.texParameteri(y,a.TEXTURE_WRAP_T,a.CLAMP_TO_EDGE),a.texParameteri(y,a.TEXTURE_MIN_FILTER,a.LINEAR),a.texParameteri(y,a.TEXTURE_MAG_FILTER,a.LINEAR),a.activeTexture(a.TEXTURE0)),a.texParameteri(y,a.TEXTURE_WRAP_S,a.CLAMP_TO_EDGE),a.texParameteri(y,a.TEXTURE_WRAP_T,a.CLAMP_TO_EDGE),a.texParameteri(y,a.TEXTURE_MIN_FILTER, +a.LINEAR),a.texParameteri(y,a.TEXTURE_MAG_FILTER,a.LINEAR)):(c.vertPosLocation=a.getAttribLocation(c,"a_vertCoord"),a.enableVertexAttribArray(c.vertPosLocation),Fa||(Fa=a.createBuffer()),Y||(Y=a.createBuffer()),na||(na=a.createBuffer()),a.bindBuffer(a.ARRAY_BUFFER,Y),a.bufferData(a.ARRAY_BUFFER,new Float32Array([0,0,1,0,1,1,0,1]),a.STATIC_DRAW),a.bindBuffer(a.ELEMENT_ARRAY_BUFFER,na),a.bufferData(a.ELEMENT_ARRAY_BUFFER,new Uint16Array([0,1,2,0,2,3]),a.STATIC_DRAW),c.perspUniform=a.getUniformLocation(c, +"u_perspMatrix"),c.cubeUniform=a.getUniformLocation(c,"u_cubeMatrix"),c.level=-1,c.currentNodes=[],c.nodeCache=[],c.nodeCacheTimestamp=0);I=a.getError();if(0!==I)throw console.log("Error: Something went wrong with WebGL!",I),{type:"webgl error"};e()}};this.destroy=function(){K!==p&&(A!==p&&K.contains(A)&&K.removeChild(A),R!==p&&K.contains(R)&&K.removeChild(R));if(a){var c=a.getExtension("WEBGL_lose_context");c&&c.loseContext()}};this.resize=function(){var g=E.devicePixelRatio||1;A.width=A.clientWidth* +g;A.height=A.clientHeight*g;a&&(1286==a.getError()&&Ea(),a.viewport(0,0,a.drawingBufferWidth,a.drawingBufferHeight),"multires"!=z&&a.uniform1f(c.aspectRatio,A.clientWidth/A.clientHeight))};this.resize();this.setPose=function(a,c){ga=[a,c]};this.render=function(g,d,l,s){var k,h=0;s===p&&(s={});s.roll&&(h=s.roll);if(ga!==p){k=ga[0];var e=ga[1],u=g,x=d,t=Math.cos(e)*Math.sin(g)*Math.sin(k)+Math.cos(g)*(Math.cos(k)*Math.cos(d)+Math.sin(e)*Math.sin(k)*Math.sin(d)),E=-Math.sin(g)*Math.sin(e)+Math.cos(g)* +Math.cos(e)*Math.sin(d);g=Math.cos(e)*Math.cos(k)*Math.sin(g)+Math.cos(g)*(-Math.cos(d)*Math.sin(k)+Math.cos(k)*Math.sin(e)*Math.sin(d));g=Math.asin(Math.max(Math.min(g,1),-1));d=Math.atan2(E,t);k=[Math.cos(u)*(Math.sin(e)*Math.sin(k)*Math.cos(x)-Math.cos(k)*Math.sin(x)),Math.cos(u)*Math.cos(e)*Math.cos(x),Math.cos(u)*(Math.cos(k)*Math.sin(e)*Math.cos(x)+Math.sin(x)*Math.sin(k))];e=[-Math.cos(g)*Math.sin(d),Math.cos(g)*Math.cos(d)];e=Math.acos(Math.max(Math.min((k[0]*e[0]+k[1]*e[1])/(Math.sqrt(k[0]* +k[0]+k[1]*k[1]+k[2]*k[2])*Math.sqrt(e[0]*e[0]+e[1]*e[1])),1),-1));0>k[2]&&(e=2*Math.PI-e);h+=e}if(a||"multires"!=z&&"cubemap"!=z){if("multires"!=z)l=2*Math.atan(Math.tan(0.5*l)/(a.drawingBufferWidth/a.drawingBufferHeight)),l=1/Math.tan(0.5*l),a.uniform1f(c.psi,d),a.uniform1f(c.theta,g),a.uniform1f(c.rot,h),a.uniform1f(c.f,l),!0===G&&"equirectangular"==z&&(a.bindTexture(a.TEXTURE_2D,c.texture),a.texImage2D(a.TEXTURE_2D,0,a.RGB,a.RGB,a.UNSIGNED_BYTE,m)),a.drawArrays(a.TRIANGLES,0,6);else{k=a.drawingBufferWidth/ +a.drawingBufferHeight;e=2*Math.atan(Math.tan(l/2)*a.drawingBufferHeight/a.drawingBufferWidth);e=1/Math.tan(e/2);k=[e/k,0,0,0,0,e,0,0,0,0,100.1/-99.9,20/-99.9,0,0,-1,0];for(e=1;em.tileResolution*Math.pow(2,e-1)*Math.tan(l/2)*0.707;)e++;c.level=e;e=[1,0,0,0,1,0,0,0,1];e=ua(e,-h,"z");e=ua(e,-g,"x");e=ua(e,d,"y");e=[e[0],e[1],e[2],0,e[3],e[4],e[5],0,e[6],e[7],e[8],0,0,0,0,1];a.uniformMatrix4fv(c.perspUniform,!1,new Float32Array(ma(k)));a.uniformMatrix4fv(c.cubeUniform, +!1,new Float32Array(ma(e)));h=[k[0]*e[0],k[0]*e[1],k[0]*e[2],0,k[5]*e[4],k[5]*e[5],k[5]*e[6],0,k[10]*e[8],k[10]*e[9],k[10]*e[10],k[11],-e[8],-e[9],-e[10],0];c.nodeCache.sort(ja);if(200c.currentNodes.length+50)for(k=c.nodeCache.splice(200,c.nodeCache.length-200),e=0;ek;k++)u=new ka(wa[k],e[k],1,0,0,m.fullpath),Ja(h,u,g,d,l);c.currentNodes.sort(Q);for(g=S.length-1;0<= +g;g--)-1===c.currentNodes.indexOf(S[g].node)&&(S[g].node.textureLoad=!1,S.splice(g,1));if(0===S.length)for(g=0;gg;g++)if(h=R.querySelector(".pnlm-"+l[g]+"face"))h.style.webkitTransform=d+s[l[g]],h.style.transform=d+s[l[g]]};this.isLoading= +function(){if(a&&"multires"==z)for(var g=0;g u_h || coord.y < -u_v + u_vo || coord.y > u_v + u_vo)\ngl_FragColor = u_backgroundColor;\nelse {\nif(u_splitImage) {\nif(coord.x < 0.0)\ngl_FragColor = texture2D(u_image0, vec2((coord.x + u_h) / u_h, (-coord.y + u_v + u_vo) / (u_v * 2.0)));\nelse\ngl_FragColor = texture2D(u_image1, vec2((coord.x + u_h) / u_h - 1.0, (-coord.y + u_v + u_vo) / (u_v * 2.0)));\n} else {\ngl_FragColor = texture2D(u_image0, vec2((coord.x + u_h) / (u_h * 2.0), (-coord.y + u_v + u_vo) / (u_v * 2.0)));\n}\n}\n}", +bb="varying mediump vec2 v_texCoord;uniform sampler2D u_sampler;void main(void) {gl_FragColor = texture2D(u_sampler, v_texCoord);}";return{renderer:function(g,l,p,s){return new Ba(g,l,p,s)}}}(window,document); +window.pannellum=function(E,g,p){function Ba(s,l){function oa(){var a=g.createElement("div");a.innerHTML="\x3c!--[if lte IE 9]>a;a++)P.push(new Image),P[a].crossOrigin=b.crossOrigin;q.load.lbox.style.display="block";q.load.lbar.style.display="none"}else if("multires"==b.type)a=JSON.parse(JSON.stringify(b.multiRes)),b.basePath&&b.multiRes.basePath&&!/^(?:[a-z]+:)?\/\//i.test(b.multiRes.basePath)? +a.basePath=b.basePath+b.multiRes.basePath:b.multiRes.basePath?a.basePath=b.multiRes.basePath:b.basePath&&(a.basePath=b.basePath),P=a;else if(!0===b.dynamic)P=b.panorama;else{if(b.panorama===p){K(b.strings.noPanoramaError);return}P=new Image}if("cubemap"==b.type)for(var n=6,c=function(){n--;0===n&&pa()},e=function(a){var ea=g.createElement("a");ea.href=F(a.target.src,!0);ea.textContent=ea.href;K(b.strings.fileAccessError.replace("%s",ea.outerHTML))},a=0;ac||65536")+12),d=function(a){var b;0<=e.indexOf(a+'="')?(b=e.substring(e.indexOf(a+'="')+a.length+2),b=b.substring(0,b.indexOf('"'))):0<=e.indexOf(a+">")&&(b=e.substring(e.indexOf(a+">")+a.length+1),b=b.substring(0, +b.indexOf("<")));return b!==p?Number(b):null},n=d("GPano:FullPanoWidthPixels"),c=d("GPano:CroppedAreaImageWidthPixels"),g=d("GPano:FullPanoHeightPixels"),h=d("GPano:CroppedAreaImageHeightPixels"),l=d("GPano:CroppedAreaTopPixels"),k=d("GPano:PoseHeadingDegrees"),m=d("GPano:PosePitchDegrees"),d=d("GPano:PoseRollDegrees");null!==n&&null!==c&&null!==g&&null!==h&&null!==l&&(0>aa.indexOf("haov")&&(b.haov=c/n*360),0>aa.indexOf("vaov")&&(b.vaov=h/g*180),0>aa.indexOf("vOffset")&&(b.vOffset=-180*((l+h/2)/g- +0.5)),null!==k&&0>aa.indexOf("northOffset")&&(b.northOffset=k,!1!==b.compass&&(b.compass=!0)),null!==m&&null!==d&&(0>aa.indexOf("horizonPitch")&&(b.horizonPitch=m),0>aa.indexOf("horizonRoll")&&(b.horizonRoll=d)))}P.src=E.URL.createObjectURL(a)});f.readAsBinaryString!==p?f.readAsBinaryString(a):f.readAsText(a)}function K(a){a===p&&(a=b.strings.genericWebGLError);q.errorMsg.innerHTML="

"+a+"

";v.load.style.display="none";q.load.box.style.display="none";q.errorMsg.style.display="table";Na=!0;H= +p;M.style.display="none";B("error",a)}function ja(a){var b=Q(a);fa.style.left=b.x+"px";fa.style.top=b.y+"px";clearTimeout(ja.t1);clearTimeout(ja.t2);fa.style.display="block";fa.style.opacity=1;ja.t1=setTimeout(function(){fa.style.opacity=0},2E3);ja.t2=setTimeout(function(){fa.style.display="none"},2500);a.preventDefault()}function Q(a){var b=s.getBoundingClientRect(),n={};n.x=(a.clientX||a.pageX)-b.left;n.y=(a.clientY||a.pageY)-b.top;return n}function ka(a){a.preventDefault();s.focus();if(H&&b.draggable){var f= +Q(a);if(b.hotSpotDebug){var n=ta(a);console.log("Pitch: "+n[0]+", Yaw: "+n[1]+", Center Pitch: "+b.pitch+", Center Yaw: "+b.yaw+", HFOV: "+b.hfov)}t();Da();b.roll=0;w.hfov=0;ha=!0;N=Date.now();xa=f.x;ya=f.y;Oa=b.yaw;Pa=b.pitch;J.classList.add("pnlm-grabbing");J.classList.remove("pnlm-grab");B("mousedown",a);G()}}function Ja(a){b.minHfov===b.hfov?da.setHfov(ra,1E3):(a=ta(a),da.lookAt(a[0],a[1],b.minHfov,1E3))}function ta(a){var f=Q(a);a=C.getCanvas();var n=a.clientWidth,c=a.clientHeight;a=f.x/n*2- +1;var c=(1-f.y/c*2)*c/n,d=1/Math.tan(b.hfov*Math.PI/360),e=Math.sin(b.pitch*Math.PI/180),g=Math.cos(b.pitch*Math.PI/180),f=d*g-c*e,n=Math.sqrt(a*a+f*f),c=180*Math.atan((c*g+d*e)/n)/Math.PI;a=180*Math.atan2(a/n,f/n)/Math.PI+b.yaw;-180>a&&(a+=360);180a.wheelDelta?1:-1):a.wheelDelta?(x(b.hfov-0.05*a.wheelDelta),w.hfov=0>a.wheelDelta?1:-1):a.detail&&(x(b.hfov+1.5*a.detail),w.hfov=0 +b.capturedKeyNumbers.indexOf(f)||(a.preventDefault(),27==f?Aa&&h():wa(f,!0))}function $(){for(var a=0;10>a;a++)r[a]=!1}function R(a){var f=a.which||a.keycode;0>b.capturedKeyNumbers.indexOf(f)||(a.preventDefault(),wa(f,!1))}function wa(a,b){var n=!1;switch(a){case 109:case 189:case 17:case 173:r[0]!=b&&(n=!0);r[0]=b;break;case 107:case 187:case 16:case 61:r[1]!=b&&(n=!0);r[1]=b;break;case 38:r[2]!=b&&(n=!0);r[2]=b;break;case 87:r[6]!=b&&(n=!0);r[6]=b;break;case 40:r[3]!=b&&(n=!0);r[3]=b;break;case 83:r[7]!= +b&&(n=!0);r[7]=b;break;case 37:r[4]!=b&&(n=!0);r[4]=b;break;case 65:r[8]!=b&&(n=!0);r[8]=b;break;case 39:r[5]!=b&&(n=!0);r[5]=b;break;case 68:r[9]!=b&&(n=!0),r[9]=b}n&&b&&(ba="undefined"!==typeof performance&&performance.now()?performance.now():Date.now(),G())}function ga(){if(H){var a=!1,f=b.pitch,n=b.yaw,c=b.hfov,d;d="undefined"!==typeof performance&&performance.now()?performance.now():Date.now();ba===p&&(ba=d);var e=(d-ba)*b.hfov/1700,e=Math.min(e,1);r[0]&&!0===b.keyboardZoom&&(x(b.hfov+(0.8*w.hfov+ +0.5)*e),a=!0);r[1]&&!0===b.keyboardZoom&&(x(b.hfov+(0.8*w.hfov-0.2)*e),a=!0);if(r[2]||r[6])b.pitch+=(0.8*w.pitch+0.2)*e,a=!0;if(r[3]||r[7])b.pitch+=(0.8*w.pitch-0.2)*e,a=!0;if(r[4]||r[8])b.yaw+=(0.8*w.yaw-0.2)*e,a=!0;if(r[5]||r[9])b.yaw+=(0.8*w.yaw+0.2)*e,a=!0;a&&(N=Date.now());if(b.autoRotate){if(0.001=b.autoRotateStopDelay&& +(b.autoRotateStopDelay=!1,Z=b.autoRotate,b.autoRotate=0))}O.pitch&&(m("pitch"),f=b.pitch);O.yaw&&(m("yaw"),n=b.yaw);O.hfov&&(m("hfov"),c=b.hfov);0f.startPosition&&n>=f.endPosition||f.endPositionb.autoRotateInactivityDelay&&!b.autoRotate&&(b.autoRotate=Z,da.lookAt(Ga,p,ra,3E3)),requestAnimationFrame(ca); +else if(C&&(C.isLoading()||!0===b.dynamic&&Ma))requestAnimationFrame(ca);else{B("animatefinished",{pitch:da.getPitch(),yaw:da.getYaw(),hfov:da.getHfov()});Ta=!1;ba=p;var a=b.autoRotateInactivityDelay-(Date.now()-N);0b.yaw&&(b.yaw+=360));a=b.yaw;var n=0;if(b.avoidShowingBackground){var c= +b.hfov/2,e=180*Math.atan2(Math.tan(c/180*Math.PI),f.width/f.height)/Math.PI;b.vaov>b.haov?Math.min(Math.cos((b.pitch-c)/180*Math.PI),Math.cos((b.pitch+c)/180*Math.PI)):n=c*(1-Math.min(Math.cos((b.pitch-e)/180*Math.PI),Math.cos((b.pitch+e)/180*Math.PI)))}var c=b.maxYaw-b.minYaw,e=-180,d=180;360>c&&(e=b.minYaw+b.hfov/2+n,d=b.maxYaw-b.hfov/2-n,cb.yaw&&(b.yaw+=360));!1!==b.autoRotate&&a!=b.yaw&&ba!== +p&&(b.autoRotate*=-1);a=2*Math.atan(Math.tan(b.hfov/180*Math.PI*0.5)/(f.width/f.height))/Math.PI*180;f=b.minPitch+a/2;n=b.maxPitch-a/2;b.maxPitch-b.minPitchX?X+=1:10===X?($a=f[2]/Math.PI*180+b.yaw,X=!0,requestAnimationFrame(ca)):(b.pitch=f[0]/Math.PI*180,b.roll=-f[1]/Math.PI*180,b.yaw=-f[2]/Math.PI*180+$a)}function va(){try{var a={};b.horizonPitch!==p&&(a.horizonPitch=b.horizonPitch*Math.PI/180);b.horizonRoll!==p&&(a.horizonRoll=b.horizonRoll*Math.PI/180);b.backgroundColor!==p&&(a.backgroundColor=b.backgroundColor);C.init(P,b.type,b.dynamic,b.haov*Math.PI/180, +b.vaov*Math.PI/180,b.vOffset*Math.PI/180,S,a);!0!==b.dynamic&&(P=p)}catch(f){if("webgl error"==f.type||"no webgl"==f.type)K();else if("webgl size error"==f.type)K(b.strings.textureSizeError.replace("%s",f.width).replace("%s",f.maxWidth));else throw K(b.strings.unknownError),f;}}function S(){if(b.sceneFadeDuration&&C.fadeImg!==p){C.fadeImg.style.opacity=0;var a=C.fadeImg;delete C.fadeImg;setTimeout(function(){M.removeChild(a);B("scenechangefadedone")},b.sceneFadeDuration)}Ia.style.display=b.compass? +"inline":"none";L();q.load.box.style.display="none";sa!==p&&(M.removeChild(sa),sa=p);H=!0;B("load");G()}function La(a){a.pitch=Number(a.pitch)||0;a.yaw=Number(a.yaw)||0;var f=g.createElement("div");f.className="pnlm-hotspot-base";f.className=a.cssClass?f.className+(" "+a.cssClass):f.className+(" pnlm-hotspot pnlm-sprite pnlm-"+D(a.type));var c=g.createElement("span");a.text&&(c.innerHTML=D(a.text));var e;if(a.video){e=g.createElement("video");var d=a.video;b.basePath&&!qa(d)&&(d=b.basePath+d);e.src= +F(d);e.controls=!0;e.style.width=a.width+"px";M.appendChild(f);c.appendChild(e)}else if(a.image){d=a.image;b.basePath&&!qa(d)&&(d=b.basePath+d);e=g.createElement("a");e.href=F(a.URL?a.URL:d,!0);e.target="_blank";c.appendChild(e);var h=g.createElement("img");h.src=F(d);h.style.width=a.width+"px";h.style.paddingTop="5px";M.appendChild(f);e.appendChild(h);c.style.maxWidth="initial"}else if(a.URL){e=g.createElement("a");e.href=F(a.URL,!0);if(a.attributes)for(d in a.attributes)d=String(d).toLowerCase().replace(/[^a-z]/g, +""),l.escapeHTML&&(d.startsWith("on")||d.includes("href"))?console.log("Hot spot attribute skipped."):e.setAttribute(d,a.attributes[d]);else e.target="_blank";M.appendChild(e);f.className+=" pnlm-pointer";c.className+=" pnlm-pointer";e.appendChild(f)}else a.sceneId&&(f.onclick=f.ontouchend=function(){f.clicked||(f.clicked=!0,y(a.sceneId,a.targetPitch,a.targetYaw,a.targetHfov));return!1},f.className+=" pnlm-pointer",c.className+=" pnlm-pointer"),M.appendChild(f);if(a.createTooltipFunc)a.createTooltipFunc(f, +a.createTooltipArgs);else if(a.text||a.video||a.image)f.classList.add("pnlm-tooltip"),f.appendChild(c),c.style.width=c.scrollWidth-20+"px",c.style.marginLeft=-(c.scrollWidth-f.offsetWidth)/2+"px",c.style.marginTop=-c.scrollHeight-12+"px";a.clickHandlerFunc&&(f.addEventListener("click",function(b){a.clickHandlerFunc(b,a.clickHandlerArgs)},"false"),f.className+=" pnlm-pointer",c.className+=" pnlm-pointer");a.div=f}function L(){Ua||(b.hotSpots?(b.hotSpots=b.hotSpots.sort(function(a,b){return a.pitch< +b.pitch}),b.hotSpots.forEach(La)):b.hotSpots=[],Ua=!0,b.hotSpots.forEach(Ca))}function d(){var a=b.hotSpots;Ua=!1;delete b.hotSpots;if(a)for(var f=0;f=a.yaw&&-90=h||(90< +a.yaw||-90>=a.yaw)&&0>=h)a.div.style.visibility="hidden";else{var l=Math.sin((-a.yaw+b.yaw)*Math.PI/180),k=Math.tan(b.hfov*Math.PI/360);a.div.style.visibility="visible";var m=C.getCanvas(),p=m.clientWidth,m=m.clientHeight,f=[-p/k*l*c/h/2,-p/k*(f*d-c*g*e)/h/2],c=Math.sin(b.roll*Math.PI/180),e=Math.cos(b.roll*Math.PI/180),f=[f[0]*e-f[1]*c,f[0]*c+f[1]*e];f[0]+=(p-a.div.offsetWidth)/2;f[1]+=(m-a.div.offsetHeight)/2;p="translate("+f[0]+"px, "+f[1]+"px) translateZ(9999px) rotate("+b.roll+"deg)";a.scale&& +(p+=" scale("+ra/b.hfov/h+")");a.div.style.webkitTransform=p;a.div.style.MozTransform=p;a.div.style.transform=p}}function I(a){b={};var f,c,e="haov vaov vOffset northOffset horizonPitch horizonRoll".split(" ");aa=[];for(f in Va)Va.hasOwnProperty(f)&&(b[f]=Va[f]);for(f in l.default)if(l.default.hasOwnProperty(f))if("strings"==f)for(c in l.default.strings)l.default.strings.hasOwnProperty(c)&&(b.strings[c]=D(l.default.strings[c]));else b[f]=l.default[f],0<=e.indexOf(f)&&aa.push(f);if(null!==a&&""!== +a&&l.scenes&&l.scenes[a]){var d=l.scenes[a];for(f in d)if(d.hasOwnProperty(f))if("strings"==f)for(c in d.strings)d.strings.hasOwnProperty(c)&&(b.strings[c]=D(d.strings[c]));else b[f]=d[f],0<=e.indexOf(f)&&aa.push(f);b.scene=a}for(f in l)if(l.hasOwnProperty(f))if("strings"==f)for(c in l.strings)l.strings.hasOwnProperty(c)&&(b.strings[c]=D(l.strings[c]));else b[f]=l[f],0<=e.indexOf(f)&&aa.push(f)}function k(a){if((a=a?a:!1)&&"preview"in b){var c=b.preview;b.basePath&&!qa(c)&&(c=b.basePath+c);sa=g.createElement("div"); +sa.className="pnlm-preview-img";sa.style.backgroundImage="url('"+F(c).replace(/"/g,"%22").replace(/'/g,"%27")+"')";M.appendChild(sa)}var c=b.title,e=b.author;a&&("previewTitle"in b&&(b.title=b.previewTitle),"previewAuthor"in b&&(b.author=b.previewAuthor));b.hasOwnProperty("title")||(q.title.innerHTML="");b.hasOwnProperty("author")||(q.author.innerHTML="");b.hasOwnProperty("title")||b.hasOwnProperty("author")||(q.container.style.display="none");v.load.innerHTML="

"+b.strings.loadButtonLabel+"

"; +q.load.boxp.innerHTML=b.strings.loadingLabel;for(var d in b)if(b.hasOwnProperty(d))switch(d){case "title":q.title.innerHTML=D(b[d]);q.container.style.display="inline";break;case "author":var h=D(b[d]);b.authorURL&&(h=g.createElement("a"),h.href=F(b.authorURL,!0),h.target="_blank",h.innerHTML=D(b[d]),h=h.outerHTML);q.author.innerHTML=b.strings.bylineLabel.replace("%s",h);q.container.style.display="inline";break;case "fallback":h=g.createElement("a");h.href=F(b[d],!0);h.target="_blank";h.textContent= +"Click here to view this panorama in an alternative viewer.";var k=g.createElement("p");k.textContent="Your browser does not support WebGL.";k.appendChild(g.createElement("br"));k.appendChild(h);q.errorMsg.innerHTML="";q.errorMsg.appendChild(k);break;case "hfov":x(Number(b[d]));break;case "autoLoad":!0===b[d]&&C===p&&(q.load.box.style.display="inline",v.load.style.display="none",oa());break;case "showZoomCtrl":v.zoom.style.display=b[d]&&!1!=b.showControls?"block":"none";break;case "showFullscreenCtrl":v.fullscreen.style.display= +b[d]&&!1!=b.showControls&&("fullscreen"in g||"mozFullScreen"in g||"webkitIsFullScreen"in g||"msFullscreenElement"in g)?"block":"none";break;case "hotSpotDebug":Wa.style.display=b[d]?"block":"none";break;case "showControls":b[d]||(v.orientation.style.display="none",v.zoom.style.display="none",v.fullscreen.style.display="none");break;case "orientationOnByDefault":b[d]&&Ra()}a&&(c?b.title=c:delete b.title,e?b.author=e:delete b.author)}function h(){if(H&&!Na)if(Aa)g.exitFullscreen?g.exitFullscreen(): +g.mozCancelFullScreen?g.mozCancelFullScreen():g.webkitCancelFullScreen?g.webkitCancelFullScreen():g.msExitFullscreen&&g.msExitFullscreen();else try{s.requestFullscreen?s.requestFullscreen():s.mozRequestFullScreen?s.mozRequestFullScreen():s.msRequestFullscreen?s.msRequestFullscreen():s.webkitRequestFullScreen()}catch(a){}}function e(a){g.fullscreenElement||g.fullscreen||g.mozFullScreen||g.webkitIsFullScreen||g.msFullscreenElement?(v.fullscreen.classList.add("pnlm-fullscreen-toggle-button-active"), +Aa=!0):(v.fullscreen.classList.remove("pnlm-fullscreen-toggle-button-active"),Aa=!1);"resize"!==a&&B("fullscreenchange",Aa);C.resize();x(b.hfov);G()}function u(a){var c=b.minHfov;"multires"==b.type&&C&&!b.multiResMinHfov&&(c=Math.min(c,C.getCanvas().width/(b.multiRes.cubeResolution/90*0.9)));if(c>b.maxHfov)return console.log("HFOV bounds do not make sense (minHfov > maxHfov)."),b.hfov;var d=b.hfov,d=ab.maxHfov?b.maxHfov:a;b.avoidShowingBackground&&C&&(a=C.getCanvas(),d=Math.min(d,360*Math.atan(Math.tan((b.maxPitch- +b.minPitch)/360*Math.PI)/a.height*a.width)/Math.PI));return d}function x(a){b.hfov=u(a);B("zoomchange",b.hfov)}function t(){O={};Z=b.autoRotate?b.autoRotate:Z;b.autoRotate=!1}function Ya(){Na&&(q.load.box.style.display="none",q.errorMsg.style.display="none",Na=!1,M.style.display="block",B("errorcleared"));H=!1;v.load.style.display="none";q.load.box.style.display="inline";oa()}function y(a,c,e,h,g){H||(g=!0);H=!1;O={};var m,q;if(b.sceneFadeDuration&&!g&&(m=C.render(b.pitch*Math.PI/180,b.yaw*Math.PI/ +180,b.hfov*Math.PI/180,{returnImage:!0}),m!==p)){g=new Image;g.className="pnlm-fade-img";g.style.transition="opacity "+b.sceneFadeDuration/1E3+"s";g.style.width="100%";g.style.height="100%";g.onload=function(){y(a,c,e,h,!0)};g.src=m;M.appendChild(g);C.fadeImg=g;return}g="same"===c?b.pitch:c;m="same"===e?b.yaw:"sameAzimuth"===e?b.yaw+(b.northOffset||0)-(l.scenes[a].northOffset||0):e;q="same"===h?b.hfov:h;d();I(a);w.yaw=w.pitch=w.hfov=0;k();g!==p&&(b.pitch=g);m!==p&&(b.yaw=m);q!==p&&(b.hfov=q);B("scenechange", +a);Ya()}function Da(){E.removeEventListener("deviceorientation",na);v.orientation.classList.remove("pnlm-orientation-button-active");X=!1}function Ra(){"function"===typeof DeviceMotionEvent.requestPermission?DeviceOrientationEvent.requestPermission().then(function(a){"granted"==a&&(X=1,E.addEventListener("deviceorientation",na),v.orientation.classList.add("pnlm-orientation-button-active"))}):(X=1,E.addEventListener("deviceorientation",na),v.orientation.classList.add("pnlm-orientation-button-active"))} +function D(a){return l.escapeHTML?String(a).split(/&/g).join("&").split('"').join(""").split("'").join("'").split("<").join("<").split(">").join(">").split("/").join("/").split("\n").join("
"):String(a).split("\n").join("
")}function F(a,b){try{var c=decodeURIComponent(ab(a)).replace(/[^\w:]/g,"").toLowerCase()}catch(d){return"about:blank"}return 0===c.indexOf("javascript:")||0===c.indexOf("vbscript:")?(console.log("Script URL removed."),"about:blank"):b&&0===c.indexOf("data:")? +(console.log("Data URI removed from link."),"about:blank"):a}function ab(a){return a.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig,function(a,b){b=b.toLowerCase();return"colon"===b?":":"#"===b.charAt(0)?"x"===b.charAt(1)?String.fromCharCode(parseInt(b.substring(2),16)):String.fromCharCode(+b.substring(1)):""})}function B(a){if(a in T)for(var b=T[a].length;0a?2*a*a:-1+(4-2*a)*a},draggable:!0,disableKeyboardCtrl:!1,crossOrigin:"anonymous",touchPanSpeedCoeffFactor:1,capturedKeyNumbers:[16,17,27,37,38,39,40,61,65,68,83,87,107,109,173,187,189],friction:0.15,strings:{loadButtonLabel:"Click to
Load
Panorama",loadingLabel:"Loading...",bylineLabel:"by %s",noPanoramaError:"No panorama image was specified.",fileAccessError:"The file %s could not be accessed.", +malformedURLError:"There is something wrong with the panorama URL.",iOS8WebGLError:"Due to iOS 8's broken WebGL implementation, only progressive encoded JPEGs work for your device (this panorama uses standard encoding).",genericWebGLError:"Your browser does not have the necessary WebGL support to display this panorama.",textureSizeError:"This panorama is too big for your device! It's %spx wide, but your device only supports images up to %spx wide. Try another device. (If you're the author, try scaling down the image.)", +unknownError:"Unknown error. Check developer console."}};s="string"===typeof s?g.getElementById(s):s;s.classList.add("pnlm-container");s.tabIndex=0;var J=g.createElement("div");J.className="pnlm-ui";s.appendChild(J);var M=g.createElement("div");M.className="pnlm-render-container";s.appendChild(M);var W=g.createElement("div");W.className="pnlm-dragfix";J.appendChild(W);var fa=g.createElement("span");fa.className="pnlm-about-msg";fa.innerHTML='Pannellum 2.5.7'; +J.appendChild(fa);W.addEventListener("contextmenu",ja);var q={},Wa=g.createElement("div");Wa.className="pnlm-sprite pnlm-hot-spot-debug-indicator";J.appendChild(Wa);q.container=g.createElement("div");q.container.className="pnlm-panorama-info";q.title=g.createElement("div");q.title.className="pnlm-title-box";q.container.appendChild(q.title);q.author=g.createElement("div");q.author.className="pnlm-author-box";q.container.appendChild(q.author);J.appendChild(q.container);q.load={};q.load.box=g.createElement("div"); +q.load.box.className="pnlm-load-box";q.load.boxp=g.createElement("p");q.load.box.appendChild(q.load.boxp);q.load.lbox=g.createElement("div");q.load.lbox.className="pnlm-lbox";q.load.lbox.innerHTML='
';q.load.box.appendChild(q.load.lbox);q.load.lbar=g.createElement("div");q.load.lbar.className="pnlm-lbar";q.load.lbarFill=g.createElement("div");q.load.lbarFill.className="pnlm-lbar-fill";q.load.lbar.appendChild(q.load.lbarFill);q.load.box.appendChild(q.load.lbar);q.load.msg= +g.createElement("p");q.load.msg.className="pnlm-lmsg";q.load.box.appendChild(q.load.msg);J.appendChild(q.load.box);q.errorMsg=g.createElement("div");q.errorMsg.className="pnlm-error-msg pnlm-info-box";J.appendChild(q.errorMsg);var v={};v.container=g.createElement("div");v.container.className="pnlm-controls-container";J.appendChild(v.container);v.load=g.createElement("div");v.load.className="pnlm-load-button";v.load.addEventListener("click",function(){k();Ya()});J.appendChild(v.load);v.zoom=g.createElement("div"); +v.zoom.className="pnlm-zoom-controls pnlm-controls";v.zoomIn=g.createElement("div");v.zoomIn.className="pnlm-zoom-in pnlm-sprite pnlm-control";v.zoomIn.addEventListener("click",function(){H&&(x(b.hfov-5),G())});v.zoom.appendChild(v.zoomIn);v.zoomOut=g.createElement("div");v.zoomOut.className="pnlm-zoom-out pnlm-sprite pnlm-control";v.zoomOut.addEventListener("click",function(){H&&(x(b.hfov+5),G())});v.zoom.appendChild(v.zoomOut);v.container.appendChild(v.zoom);v.fullscreen=g.createElement("div"); +v.fullscreen.addEventListener("click",h);v.fullscreen.className="pnlm-fullscreen-toggle-button pnlm-sprite pnlm-fullscreen-toggle-button-inactive pnlm-controls pnlm-control";(g.fullscreenEnabled||g.mozFullScreenEnabled||g.webkitFullscreenEnabled||g.msFullscreenEnabled)&&v.container.appendChild(v.fullscreen);v.orientation=g.createElement("div");v.orientation.addEventListener("click",function(a){X?Da():Ra()});v.orientation.addEventListener("mousedown",function(a){a.stopPropagation()});v.orientation.addEventListener("touchstart", +function(a){a.stopPropagation()});v.orientation.addEventListener("pointerdown",function(a){a.stopPropagation()});v.orientation.className="pnlm-orientation-button pnlm-orientation-button-inactive pnlm-sprite pnlm-controls pnlm-control";var Xa=!1;E.DeviceOrientationEvent&&"https:"==location.protocol&&0<=navigator.userAgent.toLowerCase().indexOf("mobi")&&(v.container.appendChild(v.orientation),Xa=!0);var Ia=g.createElement("div");Ia.className="pnlm-compass pnlm-controls pnlm-control";J.appendChild(Ia); +l.firstScene?I(l.firstScene):l.default&&l.default.firstScene?I(l.default.firstScene):I(null);k(!0);var ia=[],za=[];Y.prototype.multiply=function(a){return new Y(this.w*a.w-this.x*a.x-this.y*a.y-this.z*a.z,this.x*a.w+this.w*a.x+this.y*a.z-this.z*a.y,this.y*a.w+this.w*a.y+this.z*a.x-this.x*a.z,this.z*a.w+this.w*a.z+this.x*a.y-this.y*a.x)};Y.prototype.toEulerAngles=function(){var a=Math.atan2(2*(this.w*this.x+this.y*this.z),1-2*(this.x*this.x+this.y*this.y)),b=Math.asin(2*(this.w*this.y-this.z*this.x)), +c=Math.atan2(2*(this.w*this.z+this.x*this.y),1-2*(this.y*this.y+this.z*this.z));return[a,b,c]};this.isLoaded=function(){return Boolean(H)};this.getPitch=function(){return b.pitch};this.setPitch=function(a,c,d,e){N=Date.now();if(1E-6>=Math.abs(a-b.pitch))return"function"==typeof d&&d(e),this;(c=c==p?1E3:Number(c))?(O.pitch={startTime:Date.now(),startPosition:b.pitch,endPosition:a,duration:c},"function"==typeof d&&setTimeout(function(){d(e)},c)):b.pitch=a;G();return this};this.getPitchBounds=function(){return[b.minPitch, +b.maxPitch]};this.setPitchBounds=function(a){b.minPitch=Math.max(-90,Math.min(a[0],90));b.maxPitch=Math.max(-90,Math.min(a[1],90));return this};this.getYaw=function(){return(b.yaw+540)%360-180};this.setYaw=function(a,c,d,e){N=Date.now();if(1E-6>=Math.abs(a-b.yaw))return"function"==typeof d&&d(e),this;c=c==p?1E3:Number(c);a=(a+180)%360-180;c?(180=Math.abs(a-b.hfov))return"function"==typeof d&&d(e),this;(c=c==p?1E3:Number(c))?(O.hfov={startTime:Date.now(),startPosition:b.hfov,endPosition:u(a),duration:c},"function"==typeof d&&setTimeout(function(){d(e)}, +c)):x(a);G();return this};this.getHfovBounds=function(){return[b.minHfov,b.maxHfov]};this.setHfovBounds=function(a){b.minHfov=Math.max(0,a[0]);b.maxHfov=Math.max(0,a[1]);return this};this.lookAt=function(a,c,d,e,g,h){e=e==p?1E3:Number(e);a!==p&&1E-6=14" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmmirror.com/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/better-sqlite3": { + "version": "11.10.0", + "resolved": "https://registry.npmmirror.com/better-sqlite3/-/better-sqlite3-11.10.0.tgz", + "integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmmirror.com/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmmirror.com/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmmirror.com/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmmirror.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abi": { + "version": "3.87.0", + "resolved": "https://registry.npmmirror.com/node-abi/-/node-abi-3.87.0.tgz", + "integrity": "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmmirror.com/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qs": { + "version": "6.14.2", + "resolved": "https://registry.npmmirror.com/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmmirror.com/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..a94537f --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "720yun-offline", + "version": "1.0.0", + "description": "全景查看器,使用 image/ 六面图本地部署,支持统计与弹幕", + "scripts": { + "start": "node server.js", + "build": "node build.js", + "preview": "npm run build && node server.js dist" + }, + "engines": { + "node": ">=14" + }, + "dependencies": { + "better-sqlite3": "^11.6.0", + "express": "^4.21.0" + } +} diff --git a/panorama/.gitkeep b/panorama/.gitkeep new file mode 100644 index 0000000..2173d07 --- /dev/null +++ b/panorama/.gitkeep @@ -0,0 +1 @@ +# 将全景图保存为 panorama.jpg,或运行: python3 fetch_720yun.py "720yun链接" diff --git a/parse_720yun_doc.py b/parse_720yun_doc.py new file mode 100644 index 0000000..29ec0eb --- /dev/null +++ b/parse_720yun_doc.py @@ -0,0 +1,235 @@ +#!/usr/bin/env python3 +""" +从 text.md(720yun 页面保存的文档)中解析 window.data / window.json, +并解析出最终的全景图片资源 URL。 + +用法: + python3 parse_720yun_doc.py [text.md] + python3 parse_720yun_doc.py --fetch # 并请求场景 JSON,解析出所有图片 URL +""" +import re +import sys +import json +import urllib.request +from pathlib import Path + + +def read_doc(path): + with open(path, 'r', encoding='utf-8', errors='replace') as f: + return f.read() + + +def parse_window_data(html): + """解析 window.data={...}; 或 window.data = {...}(支持嵌套)""" + m = re.search(r'window\.data\s*=\s*\{', html) + if not m: + return None + start = m.end() - 1 # 从 '{' 开始 + depth = 0 + in_str = None + escape = False + i = start + while i < len(html): + c = html[i] + if escape: + escape = False + i += 1 + continue + if in_str: + if c == '\\': + escape = True + elif c == in_str: + in_str = None + i += 1 + continue + if c in '"\'': + in_str = c + i += 1 + continue + if c == '{': + depth += 1 + elif c == '}': + depth -= 1 + if depth == 0: + raw = html[start:i + 1] + try: + return json.loads(raw) + except json.JSONDecodeError: + return None + i += 1 + return None + + +def parse_window_json(html): + """解析 window.json="..."; """ + m = re.search(r'window\.json\s*=\s*["\']([^"\']+)["\']', html) + return m.group(1) if m else None + + +# 720yun 全景图实际 CDN:浏览器里能访问的域名(与 resource-t 不同,需用此域名才能正确拉取) +RESOURCE_CDN_HOST = 'ssl-panoimg130.720static.com' + + +def build_resource_base(thumb_url): + """从 thumbUrl 得到资源目录的 base URL(用于拼立方体等)。使用实际 CDN 域名以便脚本拉取与浏览器一致。""" + # thumbUrl 可能是 "/resource/prod/4ca3fae5e7x/d22jkguytw6/59446768/imgs/thumb.jpg" + # 全景图实际在 ssl-panoimg130.720static.com,用 resource-t 会拿不到或异常 + if thumb_url.startswith('http'): + base = re.sub(r'^https?://[^/]+', 'https://' + RESOURCE_CDN_HOST, thumb_url) + else: + base = 'https://' + RESOURCE_CDN_HOST + thumb_url + base = base.rsplit('/', 1)[0] + '/' + return base + + +def infer_cube_urls(resource_base): + """根据 720yun 常见命名推断立方体六面图 URL(与本地 image/mobile_*.jpg 对应)。""" + faces = ['f', 'r', 'b', 'l', 'u', 'd'] # 前 右 后 左 上 下 + return [resource_base + 'mobile_' + face + '.jpg' for face in faces] + + +# 与浏览器一致的请求头,720yun CDN 校验 Referer,否则拿不到正确数据 +def _browser_headers(referer='https://www.720yun.com/'): + return { + 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Accept': 'image/avif,image/webp,image/apng,image/*,*/*;q=0.9', + 'Referer': referer, + } + + +def fetch_tour_json(json_path, base_url='https://www.720yun.com/'): + """请求场景 JSON。json_path 为 window.json 的值,如 json/4ca3fae5e7x/.../3.json""" + url = base_url.rstrip('/') + '/' + json_path.lstrip('/') + req = urllib.request.Request(url, headers=_browser_headers()) + try: + with urllib.request.urlopen(req, timeout=15) as r: + return json.loads(r.read().decode('utf-8', errors='replace')) + except Exception as e: + return None + + +def download_to_file(url, dest_path): + """用浏览器头拉取并保存,保证与 Chrome 读取一致。""" + req = urllib.request.Request(url, headers=_browser_headers()) + with urllib.request.urlopen(req, timeout=30) as r: + dest_path.write_bytes(r.read()) + + +def extract_image_urls_from_tour(tour_data, resource_base): + """从 krpano/720 场景 JSON 中递归提取所有图片 URL。""" + urls = [] + + def collect(obj): + if isinstance(obj, dict): + for k, v in obj.items(): + if k in ('url', 'panorama', 'image', 'src', 'path', 'thumbUrl', 'basePath'): + if isinstance(v, str) and (v.startswith('http') or v.startswith('/')): + u = v if v.startswith('http') else (resource_base.rstrip('/') + v) + urls.append(u) + elif k == 'cubeMap' and isinstance(v, list): + for u in v: + if isinstance(u, str): + urls.append(u if u.startswith('http') else resource_base.rstrip('/') + '/' + u.lstrip('/')) + else: + collect(v) + elif isinstance(obj, list): + for x in obj: + collect(x) + + collect(tour_data) + return list(dict.fromkeys(urls)) + + +def main(): + doc_path = Path(__file__).resolve().parent / 'text.md' + if len(sys.argv) >= 2 and not sys.argv[1].startswith('-'): + doc_path = Path(sys.argv[1]) + do_fetch = '--fetch' in sys.argv + do_download = '--download' in sys.argv + + if not doc_path.exists(): + print('未找到文档:', doc_path) + sys.exit(1) + + html = read_doc(doc_path) + data = parse_window_data(html) + json_path = parse_window_json(html) + + if not data and not json_path: + print('未能从文档中解析出 window.data 或 window.json') + sys.exit(1) + + # 解析结果 + result = { + 'window_data': data, + 'window_json_path': json_path, + 'resource_base': None, + 'thumb_url': None, + 'inferred_cube_urls': [], + 'tour_json_url': None, + 'tour_image_urls': [], + } + + if data: + thumb = data.get('thumbUrl') or '' + if thumb and not thumb.startswith('http'): + result['thumb_url'] = 'https://thumb-t.720static.com' + thumb + else: + result['thumb_url'] = thumb or None + result['resource_base'] = build_resource_base(thumb) if thumb else None + result['tid'] = data.get('tid') + result['name'] = data.get('name') + result['sceneCount'] = data.get('sceneCount') + + if result['resource_base']: + result['inferred_cube_urls'] = infer_cube_urls(result['resource_base']) + + if json_path: + result['tour_json_url'] = 'https://www.720yun.com/' + json_path.lstrip('/') + if do_fetch and result['resource_base']: + tour = fetch_tour_json(json_path) + if tour: + result['tour_image_urls'] = extract_image_urls_from_tour(tour, result['resource_base']) + else: + print('请求场景 JSON 失败:', result['tour_json_url'], file=sys.stderr) + + # 输出:先写 JSON 汇总,再列最终图片列表 + out_path = Path(__file__).resolve().parent / 'parsed_720yun_resources.json' + with open(out_path, 'w', encoding='utf-8') as f: + json.dump(result, f, ensure_ascii=False, indent=2) + print('已写入:', out_path) + + # 最终图片资源列表(去重、合并) + all_urls = [] + if result.get('thumb_url'): + all_urls.append(('thumb', result['thumb_url'])) + for u in result.get('inferred_cube_urls', []): + all_urls.append(('cube', u)) + for u in result.get('tour_image_urls', []): + if not any(u == x for _, x in all_urls): + all_urls.append(('tour', u)) + + print('\n--- 解析出的图片资源 ---') + for kind, url in all_urls: + print(kind, url) + print('\n共', len(all_urls), '个 URL') + + if do_download and result.get('resource_base'): + out_dir = Path(__file__).resolve().parent / 'image' + out_dir.mkdir(exist_ok=True) + print('\n--- 使用浏览器头下载到 image/ ---') + for face, url in [('thumb', result.get('thumb_url'))] + list(zip(['mobile_f', 'mobile_r', 'mobile_b', 'mobile_l', 'mobile_u', 'mobile_d'], result.get('inferred_cube_urls', []))): + if not url: + continue + name = face + '.jpg' if face != 'thumb' else 'thumb.jpg' + dest = out_dir / name + try: + download_to_file(url, dest) + print('OK', name) + except Exception as e: + print('FAIL', name, e) + return result + + +if __name__ == '__main__': + main() diff --git a/parsed_720yun_resources.json b/parsed_720yun_resources.json new file mode 100644 index 0000000..f800823 --- /dev/null +++ b/parsed_720yun_resources.json @@ -0,0 +1,58 @@ +{ + "window_data": { + "thumbUrl": "/resource/prod/4ca3fae5e7x/d22jkguytw6/59446768/imgs/thumb.jpg", + "name": "未命名19:19", + "sceneType": "1", + "expireDate": null, + "memberId": 2715586, + "subChannel": "广安华蓥山旅游区", + "keywords": "3A级,海洋", + "pvCount": 167, + "sceneCount": 1, + "updateDate": 1674213698, + "operatorId": null, + "flag": "{\"defaultEnabledGyro\":0,\"dragMode\":1,\"showAuthorName\":1,\"showAutoRotateBtn\":1,\"showComment\":1,\"showCommentBtn\":1,\"showFullscreenBtn\":1,\"showGyroBtn\":1,\"showLikeBtn\":1,\"showRemarkBtn\":1,\"showSceneName\":1,\"showShareBtn\":1,\"showTourPV\":1,\"showTourRemark\":1,\"showVRBtn\":1,\"showViewSwitchBtn\":1}", + "privilege": 1, + "tid": "c8525usOunr", + "remark": null, + "publishPlatformDate": 1674213698, + "channel": { + "name": "风光/景区", + "id": 5 + }, + "templateId": 1, + "status": 0, + "publishPlatform": 1, + "selected": 0, + "version": 3, + "createDate": 1674213655, + "memberUid": "d22jkguytw6", + "expired": 1, + "id": 980994, + "shareConfig": "{}", + "likeCount": 0, + "author": { + "uid": "d22jkguytw6", + "nickname": "Alen", + "cert": 0, + "avatar": "/avatar/system/25.jpg", + "id": 2715586 + } + }, + "window_json_path": "json/4ca3fae5e7x/d22jkguytw6/c8525usOunr/3.json", + "resource_base": "https://ssl-panoimg130.720static.com/resource/prod/4ca3fae5e7x/d22jkguytw6/59446768/imgs/", + "thumb_url": "https://thumb-t.720static.com/resource/prod/4ca3fae5e7x/d22jkguytw6/59446768/imgs/thumb.jpg", + "inferred_cube_urls": [ + "https://ssl-panoimg130.720static.com/resource/prod/4ca3fae5e7x/d22jkguytw6/59446768/imgs/mobile_f.jpg", + "https://ssl-panoimg130.720static.com/resource/prod/4ca3fae5e7x/d22jkguytw6/59446768/imgs/mobile_r.jpg", + "https://ssl-panoimg130.720static.com/resource/prod/4ca3fae5e7x/d22jkguytw6/59446768/imgs/mobile_b.jpg", + "https://ssl-panoimg130.720static.com/resource/prod/4ca3fae5e7x/d22jkguytw6/59446768/imgs/mobile_l.jpg", + "https://ssl-panoimg130.720static.com/resource/prod/4ca3fae5e7x/d22jkguytw6/59446768/imgs/mobile_u.jpg", + "https://ssl-panoimg130.720static.com/resource/prod/4ca3fae5e7x/d22jkguytw6/59446768/imgs/mobile_d.jpg" + ], + "tour_json_url": "https://www.720yun.com/json/4ca3fae5e7x/d22jkguytw6/c8525usOunr/3.json", + "tour_image_urls": [], + "tid": "c8525usOunr", + "name": "未命名19:19", + "sceneCount": 1 +} \ No newline at end of file diff --git a/server.js b/server.js new file mode 100644 index 0000000..1772dd7 --- /dev/null +++ b/server.js @@ -0,0 +1,102 @@ +#!/usr/bin/env node +/** + * 静态资源 + API 服务。统计、点赞、分享、留言(弹幕)写入 SQLite。 + * 用法: node server.js [目录] 默认目录为项目根目录。 + */ +const path = require('path'); +const express = require('express'); +const db = require('./db.js'); + +const PORT = process.env.PORT || 3000; +const ROOT = path.resolve(__dirname, process.argv[2] || '.'); + +db.initDb(); + +const app = express(); +app.use(express.json()); +app.use(express.static(ROOT)); + +app.get('/api/stats', (req, res) => { + try { + res.json(db.getStats()); + } catch (e) { + res.status(500).json({ error: String(e.message) }); + } +}); + +app.post('/api/view', (req, res) => { + try { + res.json(db.incView()); + } catch (e) { + res.status(500).json({ error: String(e.message) }); + } +}); + +app.post('/api/like', (req, res) => { + try { + res.json(db.incLike()); + } catch (e) { + res.status(500).json({ error: String(e.message) }); + } +}); + +app.post('/api/share', (req, res) => { + try { + res.json(db.incShare()); + } catch (e) { + res.status(500).json({ error: String(e.message) }); + } +}); + +app.post('/api/join', (req, res) => { + try { + const viewerId = req.body && req.body.viewerId ? String(req.body.viewerId) : null; + if (!viewerId) { + return res.status(400).json({ error: 'viewerId required' }); + } + const watchingNow = db.joinViewer(viewerId); + res.json({ watchingNow }); + } catch (e) { + res.status(500).json({ error: String(e.message) }); + } +}); + +app.post('/api/leave', (req, res) => { + try { + const viewerId = req.body && req.body.viewerId ? String(req.body.viewerId) : null; + if (!viewerId) { + return res.status(400).json({ error: 'viewerId required' }); + } + const watchingNow = db.leaveViewer(viewerId); + res.json({ watchingNow }); + } catch (e) { + res.status(500).json({ error: String(e.message) }); + } +}); + +app.get('/api/comments', (req, res) => { + try { + const limit = Math.min(parseInt(req.query.limit, 10) || 100, 200); + res.json(db.getComments(limit)); + } catch (e) { + res.status(500).json({ error: String(e.message) }); + } +}); + +app.post('/api/comments', (req, res) => { + try { + const content = req.body && req.body.content != null ? String(req.body.content) : ''; + const nickname = req.body && req.body.nickname != null ? String(req.body.nickname) : ''; + if (!content.trim()) { + return res.status(400).json({ error: 'content required' }); + } + const comment = db.addComment(content.trim(), nickname.trim() || null); + res.json(comment); + } catch (e) { + res.status(500).json({ error: String(e.message) }); + } +}); + +app.listen(PORT, () => { + console.log('已启动: http://localhost:' + PORT); +}); diff --git a/text.md b/text.md new file mode 100644 index 0000000..0efa237 --- /dev/null +++ b/text.md @@ -0,0 +1,3057 @@ +(function() { + var h = {} + , mt = {} + , c = { + id: "08a05dadf3e5b6d1c99fc4d862897e31", + dm: ["720yun.com"], + js: "tongji.baidu.com/hm-web/js/", + etrk: ["%7b%22https%3a%5c%2f%5c%2f720yun.com%5c%2ft%5c%2f*%22%3a%5b%22%23app%3ediv%5b9%5d%3eimg%22%5d%7d"], + cetrk: [], + cptrk: [], + icon: '', + ctrk: ["%5b%22https%3a%5c%2f%5c%2f720yun.com%22%2c%22https%3a%5c%2f%5c%2f720yun.com%5c%2fvip%22%2c%22https%3a%5c%2f%5c%2f720yun.com%5c%2fmy%5c%2fhome%22%5d"], + vdur: 1800000, + age: 31536000000, + qiao: 0, + pt: 0, + spa: 1, + aet: '', + hca: '3D350F1E62EFCEFE', + ab: '0', + v: 1 + }; + var s = void 0 + , t = !0 + , u = null + , x = !1; + mt.cookie = {}; + mt.cookie.set = function(e, a, b) { + var k; + b.C && (k = new Date, + k.setTime(k.getTime() + b.C)); + document.cookie = e + "=" + a + (b.domain ? "; domain=" + b.domain : "") + (b.path ? "; path=" + b.path : "") + (k ? "; expires=" + k.toGMTString() : "") + (b.ec ? "; secure" : "") + } + ; + mt.cookie.get = function(e) { + return (e = RegExp("(^| )" + e + "=([^;]*)(;|$)").exec(document.cookie)) ? e[2] : u + } + ; + mt.cookie.rb = function(e, a) { + try { + var b = "Hm_ck_" + +new Date; + mt.cookie.set(b, "42", { + domain: e, + path: a, + C: s + }); + var k = "42" === mt.cookie.get(b) ? "1" : "0"; + mt.cookie.set(b, "", { + domain: e, + path: a, + C: -1 + }); + return k + } catch (d) { + return "0" + } + } + ; + mt.event = {}; + mt.event.c = function(e, a, b, k) { + e.addEventListener ? e.addEventListener(a, b, k || x) : e.attachEvent && e.attachEvent("on" + a, function(d) { + b.call(e, d) + }) + } + ; + (function() { + var e = mt.event; + mt.lang = {}; + mt.lang.i = function(a, b) { + return "[object " + b + "]" === {}.toString.call(a) + } + ; + mt.lang.j = function(a) { + return mt.lang.i(a, "Function") + } + ; + mt.lang.J = function(a) { + return mt.lang.i(a, "Object") + } + ; + mt.lang.Xb = function(a) { + return mt.lang.i(a, "Number") && isFinite(a) + } + ; + mt.lang.Z = function(a) { + return mt.lang.i(a, "String") + } + ; + mt.lang.isArray = function(a) { + return mt.lang.i(a, "Array") + } + ; + mt.lang.n = function(a) { + return a.replace ? a.replace(/'/g, "'0").replace(/\*/g, "'1").replace(/!/g, "'2") : a + } + ; + mt.lang.trim = function(a) { + return a.replace(/^\s+|\s+$/g, "") + } + ; + mt.lang.find = function(a, b, k) { + if (mt.lang.isArray(a) && mt.lang.j(b)) + for (var d = a.length, f = 0; f < d; f++) + if (f in a && b.call(k || a, a[f], f)) + return a[f]; + return u + } + ; + mt.lang.X = function(a, b) { + return mt.lang.find(a, function(k) { + return k === b + }) != u + } + ; + mt.lang.filter = function(a, b) { + var k = -1 + , d = 0 + , f = a == u ? 0 : a.length + , g = []; + if (mt.lang.j(b)) + for (; ++k < f; ) { + var l = a[k]; + b(l, k, a) && (g[d++] = l) + } + return g + } + ; + mt.lang.unique = function(a, b) { + var k = a.length, d = a.slice(0), f, g; + for (mt.lang.j(b) || (b = function(b, d) { + return b === d + } + ); 0 < --k; ) { + g = d[k]; + for (f = k; f--; ) + if (b(g, d[f])) { + d.splice(k, 1); + break + } + } + return d + } + ; + mt.lang.$b = function(a, b) { + function k(b) { + b = (d + d + Number(b).toString(2)).slice(-64); + return [parseInt(b.slice(0, 32), 2), parseInt(b.slice(-32), 2)] + } + var d = "00000000000000000000000000000000" + , f = k(a) + , g = k(b); + return parseInt((d + ((f[0] | g[0]) >>> 0).toString(2)).slice(-32) + (d + ((f[1] | g[1]) >>> 0).toString(2)).slice(-32), 2) + } + ; + mt.lang.extend = function(a) { + for (var b = Array.prototype.slice.call(arguments, 1), k = 0; k < b.length; k++) { + var d = b[k], f; + for (f in d) + Object.prototype.hasOwnProperty.call(d, f) && d[f] && (a[f] = d[f]) + } + return a + } + ; + mt.lang.Pb = function(a) { + function b(b, d) { + var a = window.history + , g = a[b]; + a[b] = function() { + g.apply(a, arguments); + mt.lang.j(d) && d() + } + } + b("pushState", function() { + a() + }); + b("replaceState", function() { + a() + }); + e.c(window, window.history.pushState ? "popstate" : "hashchange", function() { + a() + }) + } + ; + return mt.lang + } + )(); + mt.url = {}; + mt.url.f = function(e, a) { + var b = e.match(RegExp("(^|&|\\?|#)(" + a + ")=([^&#]*)(&|$|#)", "")); + return b ? b[3] : u + } + ; + mt.url.Sa = function(e) { + return (e = e.match(/^(https?:\/\/)?([^\/\?#]*)/)) ? e[2].replace(/.*@/, "") : u + } + ; + mt.url.V = function(e) { + return (e = mt.url.Sa(e)) ? e.replace(/:\d+$/, "") : e + } + ; + mt.url.pb = function(e) { + var a = document.location.href + , a = a.replace(/^https?:\/\//, ""); + return 0 === a.indexOf(e) + } + ; + mt.url.qb = function(e, a) { + e = "." + e.replace(/:\d+/, ""); + a = "." + a.replace(/:\d+/, ""); + var b = e.indexOf(a); + return -1 < b && b + a.length === e.length + } + ; + (function() { + var e = mt.lang + , a = mt.url; + mt.d = {}; + mt.d.La = function(b) { + return document.getElementById(b) + } + ; + mt.d.Wb = function(b) { + if (!b) + return u; + try { + b = String(b); + if (0 === b.indexOf("!HMCQ!")) + return b; + if (0 === b.indexOf("!HMCC!")) + return document.querySelector(b.substring(6, b.length)); + for (var k = b.split(">"), d = document.body, a = k.length - 1; 0 <= a; a--) + if (-1 < k[a].indexOf("#")) { + var g = k[a].split("#")[1]; + (d = document.getElementById(g)) || (d = document.getElementById(decodeURIComponent(g))); + k = k.splice(a + 1, k.length - (a + 1)); + break + } + for (b = 0; d && b < k.length; ) { + var l = String(k[b]).toLowerCase(); + if (!("html" === l || "body" === l)) { + var a = 0 + , e = k[b].match(/\[(\d+)\]/i) + , g = []; + if (e) + a = e[1] - 1, + l = l.split("[")[0]; + else if (1 !== d.childNodes.length) { + for (var p = 0, n = 0, m = d.childNodes.length; n < m; n++) { + var q = d.childNodes[n]; + 1 === q.nodeType && q.nodeName.toLowerCase() === l && p++; + if (1 < p) + return u + } + if (1 !== p) + return u + } + for (p = 0; p < d.childNodes.length; p++) + 1 === d.childNodes[p].nodeType && d.childNodes[p].nodeName.toLowerCase() === l && g.push(d.childNodes[p]); + if (!g[a]) + return u; + d = g[a] + } + b++ + } + return d + } catch (v) { + return u + } + } + ; + mt.d.fa = function(b, a) { + var d = [] + , f = []; + if (!b) + return f; + for (; b.parentNode != u; ) { + for (var g = 0, l = 0, e = b.parentNode.childNodes.length, p = 0; p < e; p++) { + var n = b.parentNode.childNodes[p]; + if (n.nodeName === b.nodeName && (g++, + n === b && (l = g), + 0 < l && 1 < g)) + break + } + if ((e = "" !== b.id) && a) { + d.unshift("#" + encodeURIComponent(b.id)); + break + } else + e && (e = "#" + encodeURIComponent(b.id), + e = 0 < d.length ? e + ">" + d.join(">") : e, + f.push(e)), + d.unshift(encodeURIComponent(String(b.nodeName).toLowerCase()) + (1 < g ? "[" + l + "]" : "")); + b = b.parentNode + } + f.push(d.join(">")); + return f + } + ; + mt.d.Xa = function(b) { + return (b = mt.d.fa(b, t)) && b.length ? String(b[0]) : "" + } + ; + mt.d.Wa = function(b) { + return mt.d.fa(b, x) + } + ; + mt.d.Ma = function(b) { + var a; + for (a = "A"; (b = b.parentNode) && 1 == b.nodeType; ) + if (b.tagName == a) + return b; + return u + } + ; + mt.d.Pa = function(b) { + return 9 === b.nodeType ? b : b.ownerDocument || b.document + } + ; + mt.d.Ua = function(b) { + var a = { + top: 0, + left: 0 + }; + if (!b) + return a; + var d = mt.d.Pa(b).documentElement; + "undefined" !== typeof b.getBoundingClientRect && (a = b.getBoundingClientRect()); + return { + top: a.top + (window.pageYOffset || d.scrollTop) - (d.clientTop || 0), + left: a.left + (window.pageXOffset || d.scrollLeft) - (d.clientLeft || 0) + } + } + ; + mt.d.hc = function(b, a) { + if (b) + for (var d = b.childNodes, f = 0, g = d.length; f < g; f++) { + var e = d[f]; + if (e && 3 === e.nodeType) + return d = e.textContent || e.innerText || e.nodeValue || "", + e.textContent ? e.textContent = a : e.innerText ? e.innerText = a : e.nodeValue = a, + d + } + } + ; + mt.d.gc = function(b, a) { + if (!b) + return {}; + var d = {}; + a = a || {}; + for (var f in a) + a.hasOwnProperty(f) && a[f] !== s && (d[f] = b.getAttribute(f) || "", + b.setAttribute(f, a[f])); + return d + } + ; + mt.d.getAttribute = function(b, a) { + var d = b.getAttribute && b.getAttribute(a) || u; + if (!d && b.attributes && b.attributes.length) + for (var f = b.attributes, e = f.length, l = 0; l < e; l++) + f[l].nodeName === a && (d = f[l].nodeValue); + return d + } + ; + mt.d.Qa = function(b) { + var a = "document"; + b.tagName !== s && (a = b.tagName); + return a.toLowerCase() + } + ; + mt.d.Za = function(b) { + var a = ""; + b.textContent ? a = e.trim(b.textContent) : b.innerText && (a = e.trim(b.innerText)); + a && (a = a.replace(/\s+/g, " ").substring(0, 255)); + return a + } + ; + mt.d.Vb = function(b, k) { + var d; + e.Z(b) && 0 === String(b).indexOf("!HMCQ!") ? (d = String(b), + d = a.f(document.location.href, d.substring(6, d.length))) : e.Z(b) || (d = mt.d.Qa(b), + "input" === d && k && ("button" === b.type || "submit" === b.type) ? d = e.trim(b.value) || "" : "input" === d && !k && "password" !== b.type ? d = e.trim(b.value) || "" : "img" === d ? (d = mt.d.getAttribute, + d = d(b, "alt") || d(b, "title") || d(b, "src")) : d = "body" === d || "html" === d ? ["(hm-default-content-for-", d, ")"].join("") : mt.d.Za(b)); + return String(d || "").substring(0, 255) + } + ; + (function() { + (mt.d.bc = function() { + function b() { + if (!b.K) { + b.K = t; + for (var a = 0, d = f.length; a < d; a++) + f[a]() + } + } + function a() { + try { + document.documentElement.doScroll("left") + } catch (d) { + setTimeout(a, 1); + return + } + b() + } + var d = x, f = [], e; + document.addEventListener ? e = function() { + document.removeEventListener("DOMContentLoaded", e, x); + b() + } + : document.attachEvent && (e = function() { + "complete" === document.readyState && (document.detachEvent("onreadystatechange", e), + b()) + } + ); + (function() { + if (!d) + if (d = t, + "complete" === document.readyState) + b.K = t; + else if (document.addEventListener) + document.addEventListener("DOMContentLoaded", e, x), + window.addEventListener("load", b, x); + else if (document.attachEvent) { + document.attachEvent("onreadystatechange", e); + window.attachEvent("onload", b); + var f = x; + try { + f = window.frameElement == u + } catch (r) {} + document.documentElement.doScroll && f && a() + } + } + )(); + return function(a) { + b.K ? a() : f.push(a) + } + }()).K = x + } + )(); + return mt.d + } + )(); + (function() { + var e = mt.event; + mt.e = {}; + mt.e.mb = /msie (\d+\.\d+)/i.test(navigator.userAgent); + mt.e.cookieEnabled = navigator.cookieEnabled; + mt.e.javaEnabled = navigator.javaEnabled(); + mt.e.language = navigator.language || navigator.browserLanguage || navigator.systemLanguage || navigator.userLanguage || ""; + mt.e.Ab = (window.screen.width || 0) + "x" + (window.screen.height || 0); + mt.e.colorDepth = window.screen.colorDepth || 0; + mt.e.Ya = function() { + var a; + a = a || document; + return parseInt(window.pageYOffset || a.documentElement.scrollTop || a.body && a.body.scrollTop || 0, 10) + } + ; + mt.e.$a = function() { + var a = document; + return parseInt(window.innerHeight || a.documentElement.clientHeight || a.body && a.body.clientHeight || 0, 10) + } + ; + mt.e.W = function() { + return mt.e.Ya() + mt.e.$a() + } + ; + mt.e.sa = 0; + mt.e.bb = function() { + var a = document; + return parseInt(window.innerWidth || a.documentElement.clientWidth || a.body.offsetWidth || 0, 10) + } + ; + mt.e.orientation = 0; + (function() { + function a() { + var a = 0; + window.orientation !== s && (a = window.orientation); + screen && (screen.orientation && screen.orientation.angle !== s) && (a = screen.orientation.angle); + mt.e.orientation = a; + mt.e.sa = mt.e.bb() + } + a(); + e.c(window, "orientationchange", a) + } + )(); + return mt.e + } + )(); + mt.w = {}; + mt.w.parse = function(e) { + return (new Function("return (" + e + ")"))() + } + ; + mt.w.stringify = function() { + function e(a) { + /["\\\x00-\x1f]/.test(a) && (a = a.replace(/["\\\x00-\x1f]/g, function(a) { + var f = b[a]; + if (f) + return f; + f = a.charCodeAt(); + return "\\u00" + Math.floor(f / 16).toString(16) + (f % 16).toString(16) + })); + return '"' + a + '"' + } + function a(a) { + return 10 > a ? "0" + a : a + } + var b = { + "\b": "\\b", + "\t": "\\t", + "\n": "\\n", + "\f": "\\f", + "\r": "\\r", + '"': '\\"', + "\\": "\\\\" + }; + return function(b) { + switch (typeof b) { + case "undefined": + return "undefined"; + case "number": + return isFinite(b) ? String(b) : "null"; + case "string": + return e(b); + case "boolean": + return String(b); + default: + if (b === u) + return "null"; + if (b instanceof Array) { + var d = ["["], f = b.length, g, l, r; + for (l = 0; l < f; l++) + switch (r = b[l], + typeof r) { + case "undefined": + case "function": + case "unknown": + break; + default: + g && d.push(","), + d.push(mt.w.stringify(r)), + g = 1 + } + d.push("]"); + return d.join("") + } + if (b instanceof Date) + return '"' + b.getFullYear() + "-" + a(b.getMonth() + 1) + "-" + a(b.getDate()) + "T" + a(b.getHours()) + ":" + a(b.getMinutes()) + ":" + a(b.getSeconds()) + '"'; + g = ["{"]; + l = mt.w.stringify; + for (f in b) + if (Object.prototype.hasOwnProperty.call(b, f)) + switch (r = b[f], + typeof r) { + case "undefined": + case "unknown": + case "function": + break; + default: + d && g.push(","), + d = 1, + g.push(l(f) + ":" + l(r)) + } + g.push("}"); + return g.join("") + } + } + }(); + mt.localStorage = {}; + mt.localStorage.Q = function() { + if (!mt.localStorage.g) + try { + mt.localStorage.g = document.createElement("input"), + mt.localStorage.g.type = "hidden", + mt.localStorage.g.style.display = "none", + mt.localStorage.g.addBehavior("#default#userData"), + document.getElementsByTagName("head")[0].appendChild(mt.localStorage.g) + } catch (e) { + return x + } + return t + } + ; + mt.localStorage.set = function(e, a, b) { + var k = new Date; + k.setTime(k.getTime() + (b || 31536E6)); + try { + window.localStorage ? (a = k.getTime() + "|" + a, + window.localStorage.setItem(e, a)) : mt.localStorage.Q() && (mt.localStorage.g.expires = k.toUTCString(), + mt.localStorage.g.load(document.location.hostname), + mt.localStorage.g.setAttribute(e, a), + mt.localStorage.g.save(document.location.hostname)) + } catch (d) {} + } + ; + mt.localStorage.get = function(e) { + if (window.localStorage) { + if (e = window.localStorage.getItem(e)) { + var a = e.indexOf("|") + , b = e.substring(0, a) - 0; + if (b && b > (new Date).getTime()) + return e.substring(a + 1) + } + } else if (mt.localStorage.Q()) + try { + return mt.localStorage.g.load(document.location.hostname), + mt.localStorage.g.getAttribute(e) + } catch (k) {} + return u + } + ; + mt.localStorage.remove = function(e) { + if (window.localStorage) + window.localStorage.removeItem(e); + else if (mt.localStorage.Q()) + try { + mt.localStorage.g.load(document.location.hostname), + mt.localStorage.g.removeAttribute(e), + mt.localStorage.g.save(document.location.hostname) + } catch (a) {} + } + ; + mt.sessionStorage = {}; + mt.sessionStorage.set = function(e, a) { + try { + window.sessionStorage && window.sessionStorage.setItem(e, a) + } catch (b) {} + } + ; + mt.sessionStorage.get = function(e) { + try { + return window.sessionStorage ? window.sessionStorage.getItem(e) : u + } catch (a) { + return u + } + } + ; + mt.sessionStorage.remove = function(e) { + try { + window.sessionStorage && window.sessionStorage.removeItem(e) + } catch (a) {} + } + ; + (function() { + var e = mt.w; + mt.A = {}; + mt.A.log = function(a, b) { + var e = new Image + , d = "mini_tangram_log_" + Math.floor(2147483648 * Math.random()).toString(36); + window[d] = e; + e.onload = function() { + e.onload = u; + e = window[d] = u; + b && b(a) + } + ; + e.src = a + } + ; + mt.A.get = function(a, b) { + return mt.A.wa({ + url: a, + method: "GET", + data: b.data, + timeout: b.timeout, + noCache: t, + success: b.success, + fail: b.fail + }) + } + ; + mt.A.wa = function(a) { + function b(a) { + var b = [], d; + for (d in a) + a.hasOwnProperty(d) && b.push(encodeURIComponent(d) + "=" + encodeURIComponent(a[d])); + return b.join("&") + } + function k(b) { + var d = a[b]; + if (d) + if (q && clearTimeout(q), + "success" !== b) + d && d(m); + else { + var f; + try { + f = e.parse(m.responseText) + } catch (g) { + d && d(m); + return + } + d && d(m, f) + } + } + a = a || {}; + var d = a.data; + "object" === typeof d && (d = b(a.data || {})); + var f = a.url, g = (a.method || "GET").toUpperCase(), l = a.headers || {}, r = a.timeout || 0, p = a.noCache || x, n = a.withCredentials || x, m, q; + try { + a: if (window.XMLHttpRequest) + m = new XMLHttpRequest; + else { + try { + m = new ActiveXObject("Microsoft.XMLHTTP"); + break a + } catch (v) {} + m = s + } + "GET" === g && (d && (f += (0 <= f.indexOf("?") ? "&" : "?") + d, + d = u), + p && (f += (0 <= f.indexOf("?") ? "&" : "?") + "b" + +new Date + "=1")); + m.open(g, f, t); + m.onreadystatechange = function() { + if (4 === m.readyState) { + var a = 0; + try { + a = m.status + } catch (b) { + k("fail"); + return + } + 200 <= a && 300 > a || 304 === a || 1223 === a ? k("success") : k("fail") + } + } + ; + for (var w in l) + l.hasOwnProperty(w) && m.setRequestHeader(w, l[w]); + n && (m.withCredentials = t); + r && (q = setTimeout(function() { + m.onreadystatechange = function() {} + ; + m.abort(); + k("fail") + }, r)); + m.send(d) + } catch (A) { + k("fail") + } + return m + } + ; + return mt.A + } + )(); + h.o = { + kb: "http://tongji.baidu.com/hm-web/welcome/ico", + aa: "hm.baidu.com/hm.gif", + xa: /^(tongji|hmcdn).baidu.com$/, + Hb: "tongji.baidu.com", + hb: "hmmd", + ib: "hmpl", + Kb: "utm_medium", + gb: "hmkw", + Mb: "utm_term", + eb: "hmci", + Jb: "utm_content", + jb: "hmsr", + Lb: "utm_source", + fb: "hmcu", + Ib: "utm_campaign", + ka: 0, + B: Math.round(+new Date / 1E3), + protocol: "https:" === document.location.protocol ? "https:" : "http:", + L: "https:", + Da: 6E5, + dc: 5E3, + Ea: 5, + ca: 1024, + G: 2147483647, + ra: "hca cc cf ci ck cl cm cp cu cw ds vl ep et ja ln lo lt rnd si su v cv lv api sn r ww p u tt".split(" "), + ga: t, + Qb: { + id: "data-hm-id", + Ub: "data-hm-class", + kc: "data-hm-xpath", + content: "data-hm-content", + ic: "data-hm-tag", + link: "data-hm-link" + }, + Sb: "data-hm-enabled", + Rb: "data-hm-disabled", + xb: "https://hmcdn.baidu.com/static/tongji/plugins/", + na: ["UrlChangeTracker"], + Ob: { + ac: 0, + jc: 1, + Yb: 2 + }, + Zb: "https://fclog.baidu.com/log/ocpcagl?type=behavior&emd=euc" + }; + (function() { + var e = { + t: {}, + c: function(a, b) { + this.t[a] = this.t[a] || []; + this.t[a].push(b) + }, + k: function(a, b) { + this.t[a] = this.t[a] || []; + for (var e = this.t[a].length, d = 0; d < e; d++) + this.t[a][d](b) + } + }; + return h.s = e + } + )(); + (function() { + var e = mt.lang + , a = /^https?:\/\// + , b = { + Oa: function(a) { + var b; + try { + b = JSON.parse(decodeURIComponent(a[0])) + } catch (f) {} + return b + }, + la: function(a, d) { + return b.ma(h.b && h.b.a && h.b.a.u, a, d) || b.ma(document.location.href, a, d) + }, + ma: function(b, d, f) { + if (b === s) + return x; + a.test(d) || (b = b.replace(a, "")); + d = d.replace(/\/$/, ""); + b = b.replace(/\/$/, ""); + f && (b = b.replace(/^(https?:\/\/)?www\./, "$1")); + return RegExp("^" + d.replace(/[?.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$").test(b) + }, + H: function(a, d) { + var f = b.Oa(a); + if (!e.i(f, "Undefined")) { + if (e.isArray(f)) { + for (var g = 0; g < f.length; g++) + if (b.la(f[g], d)) + return t; + return x + } + if (e.J(f)) { + var g = [], l; + for (l in f) + f.hasOwnProperty(l) && b.la(l, d) && (g = g.concat(f[l])); + return g + } + } + } + }; + return h.S = b + } + )(); + (function() { + function e(b, e) { + var d = document.createElement("script"); + d.charset = "utf-8"; + a.j(e) && (d.readyState ? d.onreadystatechange = function() { + if ("loaded" === d.readyState || "complete" === d.readyState) + d.onreadystatechange = u, + e() + } + : d.onload = function() { + e() + } + ); + d.src = b; + var f = document.getElementsByTagName("script")[0]; + f.parentNode.insertBefore(d, f) + } + var a = mt.lang; + return h.load = e + } + )(); + (function() { + var e = h.o + , a = { + D: function() { + if ("" !== c.icon) { + var a = c.icon.split("|") + , k = e.kb + "?s=" + c.id + , d = "https://hmcdn.baidu.com/static" + a[0] + ".gif"; + document.write("swf" === a[1] || "gif" === a[1] ? '' : '' + a[0] + "") + } + } + }; + h.s.c("pv-b", a.D); + return a + } + )(); + (function() { + var e = mt.url + , a = mt.cookie + , b = mt.localStorage + , k = mt.sessionStorage + , d = { + getData: function(d) { + try { + return a.get(d) || k.get(d) || b.get(d) + } catch (e) {} + }, + setData: function(f, e, l) { + try { + a.set(f, e, { + domain: d.I(), + path: d.U(), + C: l + }), + l ? b.set(f, e, l) : k.set(f, e) + } catch (r) {} + }, + removeData: function(e) { + try { + a.set(e, "", { + domain: d.I(), + path: d.U(), + C: -1 + }), + k.remove(e), + b.remove(e) + } catch (g) {} + }, + I: function() { + for (var a = document.location.hostname, b = 0, d = c.dm.length; b < d; b++) + if (e.qb(a, c.dm[b])) + return c.dm[b].replace(/(:\d+)?[/?#].*/, ""); + return a + }, + U: function() { + for (var a = 0, b = c.dm.length; a < b; a++) { + var d = c.dm[a]; + if (-1 < d.indexOf("/") && e.pb(d)) + return d.replace(/^[^/]+(\/.*)/, "$1") + "/" + } + return "/" + } + }; + return h.R = d + } + )(); + (function() { + var e = mt.lang + , a = mt.d + , b = h.S + , k = { + Ha: function(d, e) { + return function(g) { + var l = g.target || g.srcElement; + if (l) { + var r = b.H(e) || [] + , p = l.getAttribute(d.P); + g = g.clientX + ":" + g.clientY; + if (p && p === g) + l.removeAttribute(d.P); + else if (0 < r.length && (l = a.Wa(l)) && l.length) + if (r = l.length, + p = l[l.length - 1], + 1E4 > r * p.split(">").length) + for (p = 0; p < r; p++) + k.qa(d, l[p]); + else + k.qa(d, p) + } + } + }, + qa: function(a, b) { + for (var g = {}, l = String(b).split(">").length, k = 0; k < l; k++) + g[b] = "", + /\[1\]$/.test(b) && (g[b.substring(0, b.lastIndexOf("["))] = ""), + /\]$/.test(b) || (g[b + "[1]"] = ""), + b = b.substring(0, b.lastIndexOf(">")); + a && (e.J(a) && a.ba) && a.ba(g) + }, + zb: function(a, b) { + return function(e) { + (e.target || e.srcElement).setAttribute(a.P, e.clientX + ":" + e.clientY); + a && a.N && (b ? a.N(b) : a.N("#" + encodeURIComponent(this.id), e.type)) + } + } + }; + return h.Ia = k + } + )(); + (function() { + var e = mt.d + , a = mt.event + , b = h.S + , k = h.Ia + , d = { + P: "HM_fix", + ua: function() { + a.c(document, "click", k.Ha(d, c.etrk), t); + if (!document.addEventListener) + for (var f = b.H(c.etrk) || [], g = 0; g < f.length; g++) { + var l = f[g]; + -1 === l.indexOf(">") && (0 === l.indexOf("#") && (l = l.substring(1)), + (l = e.La(l)) && a.c(l, "click", k.zb(d), t)) + } + }, + ba: function(a) { + for (var e = b.H(c.etrk) || [], k = 0; k < e.length; k++) { + var r = e[k]; + a.hasOwnProperty(r) && d.N(r) + } + }, + N: function(a, b) { + h.b.a.et = 1; + h.b.a.ep = "{id:" + a + ",eventType:" + (b || "click") + "}"; + h.b.m() + } + }; + h.s.c("pv-b", d.ua); + return d + } + )(); + (function() { + var e = mt.d + , a = mt.lang + , b = mt.event + , k = mt.e + , d = h.o + , f = h.S + , g = [] + , l = { + ta: function() { + c.ctrk && 0 < c.ctrk.length && (b.c(document, "mouseup", l.Ca()), + b.c(window, "unload", function() { + l.M() + }), + setInterval(function() { + l.M() + }, d.Da)) + }, + Ca: function() { + return function(a) { + if (f.H(c.ctrk, t) && (a = l.Na(a), + "" !== a)) { + var b = (d.L + "//" + d.aa + "?" + h.b.pa().replace(/ep=[^&]*/, "ep=" + encodeURIComponent(a))).length; + b + (d.G + "").length > d.ca || (b + encodeURIComponent(g.join("!") + (g.length ? "!" : "")).length + (d.G + "").length > d.ca && l.M(), + g.push(a), + (g.length >= d.Ea || /\*a\*/.test(a)) && l.M()) + } + } + }, + Na: function(b) { + var d = b.target || b.srcElement, f, m; + k.mb ? (m = Math.max(document.documentElement.scrollTop, document.body.scrollTop), + f = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft), + f = b.clientX + f, + m = b.clientY + m) : (f = b.pageX, + m = b.pageY); + b = l.Ta(b, d, f, m); + var q = window.innerWidth || document.documentElement.clientWidth || document.body.offsetWidth; + switch (c.align) { + case 1: + f -= q / 2; + break; + case 2: + f -= q + } + q = []; + q.push(f); + q.push(m); + q.push(b.ub); + q.push(b.vb); + q.push(b.yb); + q.push(a.n(b.wb)); + q.push(b.Nb); + q.push(b.cb); + (d = "a" === (d.tagName || "").toLowerCase() ? d : e.Ma(d)) ? (q.push("a"), + q.push(a.n(encodeURIComponent(d.href)))) : q.push("b"); + return q.join("*") + }, + Ta: function(b, d, f, m) { + b = e.Xa(d); + var q = 0 + , g = 0 + , w = 0 + , l = 0; + if (d && (q = d.offsetWidth || d.clientWidth, + g = d.offsetHeight || d.clientHeight, + l = e.Ua(d), + w = l.left, + l = l.top, + a.j(d.getBBox) && (g = d.getBBox(), + q = g.width, + g = g.height), + "html" === (d.tagName || "").toLowerCase())) + q = Math.max(q, d.clientWidth), + g = Math.max(g, d.clientHeight); + return { + ub: Math.round(100 * ((f - w) / q)), + vb: Math.round(100 * ((m - l) / g)), + yb: k.orientation, + wb: b, + Nb: q, + cb: g + } + }, + M: function() { + 0 !== g.length && (h.b.a.et = 2, + h.b.a.ep = g.join("!"), + h.b.m(), + g = []) + } + }; + h.s.c("pv-b", l.ta); + return l + } + )(); + (function() { + function e() { + return function() { + h.b.a.et = 3; + h.b.a.ep = h.T.Va() + "," + h.T.Ra(); + h.b.a.hca = c.hca; + h.b.m() + } + } + function a() { + clearTimeout(C); + var b; + w && (b = "visible" == document[w]); + A && (b = !document[A]); + l = "undefined" == typeof b ? t : b; + if ((!g || !r) && l && p) + v = t, + m = +new Date; + else if (g && r && (!l || !p)) + v = x, + q += +new Date - m; + g = l; + r = p; + C = setTimeout(a, 100) + } + function b(b) { + var a = document + , d = ""; + if (b in a) + d = b; + else + for (var m = ["webkit", "ms", "moz", "o"], e = 0; e < m.length; e++) { + var f = m[e] + b.charAt(0).toUpperCase() + b.slice(1); + if (f in a) { + d = f; + break + } + } + return d + } + function k(b) { + if (!("focus" == b.type || "blur" == b.type) || !(b.target && b.target != window)) + p = "focus" == b.type || "focusin" == b.type ? t : x, + a() + } + var d = mt.event, f = h.s, g = t, l = t, r = t, p = t, n = +new Date, m = n, q = 0, v = t, w = b("visibilityState"), A = b("hidden"), C; + a(); + (function() { + var b = w.replace(/[vV]isibilityState/, "visibilitychange"); + d.c(document, b, a); + d.c(window, "pageshow", a); + d.c(window, "pagehide", a); + "object" == typeof document.onfocusin ? (d.c(document, "focusin", k), + d.c(document, "focusout", k)) : (d.c(window, "focus", k), + d.c(window, "blur", k)) + } + )(); + h.T = { + Va: function() { + return +new Date - n + }, + Ra: function() { + return v ? +new Date - m + q : q + } + }; + f.c("pv-b", function() { + d.c(window, "unload", e()) + }); + f.c("duration-send", e()); + f.c("duration-done", function() { + m = n = +new Date; + q = 0 + }); + return h.T + } + )(); + (function() { + var e = mt.lang + , a = h.o + , b = h.load + , k = h.R + , d = { + lb: function(d) { + if ((window._dxt === s || e.i(window._dxt, "Array")) && "undefined" !== typeof h.b) { + var g = k.I(); + b([a.protocol, "//datax.baidu.com/x.js?si=", c.id, "&dm=", encodeURIComponent(g)].join(""), d) + } + }, + Gb: function(b) { + if (e.i(b, "String") || e.i(b, "Number")) + window._dxt = window._dxt || [], + window._dxt.push(["_setUserId", b]) + } + }; + return h.Fa = d + } + )(); + (function() { + function e(a) { + for (var d in a) + if ({}.hasOwnProperty.call(a, d)) { + var f = a[d]; + b.J(f) || b.isArray(f) ? e(f) : a[d] = String(f) + } + } + var a = mt.url + , b = mt.lang + , k = mt.w + , d = mt.e + , f = h.o + , g = h.s + , l = h.Fa + , r = h.load + , p = h.R + , n = { + F: [], + O: 0, + Y: x, + D: function() { + n.h = 0; + g.c("pv-b", function() { + n.Ga(); + n.Ja() + }); + g.c("pv-d", function() { + n.Ka() + }); + g.c("stag-b", function() { + h.b.a.api = n.h || n.O ? n.h + "_" + n.O : "" + }); + g.c("stag-d", function() { + h.b.a.api = 0; + n.h = 0; + n.O = 0 + }) + }, + Ga: function() { + var a = window._hmt || []; + if (!a || b.i(a, "Array")) + window._hmt = { + id: c.id, + cmd: {}, + push: function() { + for (var a = window._hmt, d = 0; d < arguments.length; d++) { + var e = arguments[d]; + b.i(e, "Array") && (a.cmd[a.id].push(e), + "_setAccount" === e[0] && (1 < e.length && /^[0-9a-f]{31,32}$/.test(e[1])) && (e = e[1], + a.id = e, + a.cmd[e] = a.cmd[e] || [])) + } + } + }, + window._hmt.cmd[c.id] = [], + window._hmt.push.apply(window._hmt, a) + }, + Ja: function() { + var b = window._hmt; + if (b && b.cmd && b.cmd[c.id]) + for (var a = b.cmd[c.id], d = /^_track(Event|Order)$/, e = 0, f = a.length; e < f; e++) { + var g = a[e]; + d.test(g[0]) ? n.F.push(g) : n.$(g) + } + b.cmd[c.id] = { + push: n.$ + } + }, + Ka: function() { + if (0 < n.F.length) + for (var b = 0, a = n.F.length; b < a; b++) + n.$(n.F[b]); + n.F = u + }, + $: function(a) { + var d = a[0]; + if (n.hasOwnProperty(d) && b.j(n[d])) + n[d](a) + }, + _setAccount: function(b) { + 1 < b.length && /^[0-9a-f]{31,32}$/.test(b[1]) && (n.h |= 1) + }, + _setAutoPageview: function(b) { + if (1 < b.length && (b = b[1], + x === b || t === b)) + n.h |= 2, + h.b.ha = b + }, + _trackPageview: function(b) { + 1 < b.length && (b[1].charAt && "/" === b[1].charAt(0)) && (n.h |= 4, + h.b.a.sn = h.b.ea(), + h.b.a.et = 0, + h.b.a.ep = "", + h.b.a.vl = d.W(), + n.Y || (h.b.a.su = h.b.a.u || document.location.href), + h.b.a.u = f.protocol + "//" + document.location.host + b[1], + h.b.m(), + h.b.sb = +new Date) + }, + _trackEvent: function(a) { + 2 < a.length && (n.h |= 8, + h.b.a.et = 4, + h.b.a.ep = b.n(a[1]) + "*" + b.n(a[2]) + (a[3] ? "*" + b.n(a[3]) : "") + (a[4] ? "*" + b.n(a[4]) : ""), + h.b.m()) + }, + _setCustomVar: function(a) { + if (!(4 > a.length)) { + var d = a[1] + , e = a[4] || 3; + if (0 < d && 6 > d && 0 < e && 4 > e) { + n.O++; + for (var f = (h.b.a.cv || "*").split("!"), g = f.length; g < d - 1; g++) + f.push("*"); + f[d - 1] = e + "*" + b.n(a[2]) + "*" + b.n(a[3]); + h.b.a.cv = f.join("!"); + a = h.b.a.cv.replace(/[^1](\*[^!]*){2}/g, "*").replace(/((^|!)\*)+$/g, ""); + "" !== a ? p.setData("Hm_cv_" + c.id, encodeURIComponent(a), c.age) : p.removeData("Hm_cv_" + c.id) + } + } + }, + _setReferrerOverride: function(a) { + 1 < a.length && (a = a[1], + b.i(a, "String") ? (h.b.a.su = "/" === a.charAt(0) ? f.protocol + "//" + window.location.host + a : a, + n.Y = t) : n.Y = x) + }, + _trackOrder: function(a) { + a = a[1]; + b.J(a) && (e(a), + n.h |= 16, + h.b.a.et = 94, + h.b.a.ep = k.stringify(a), + h.b.m()) + }, + _setDataxId: function(a) { + a = a[1]; + l.lb(); + l.Gb(a) + }, + _setAutoTracking: function(a) { + if (1 < a.length && (a = a[1], + x === a || t === a)) + h.b.ia = a + }, + _trackPageDuration: function(a) { + 1 < a.length ? (a = a[1], + 2 === String(a).split(",").length && (h.b.a.et = 3, + h.b.a.ep = a, + h.b.m())) : g.k("duration-send"); + g.k("duration-done") + }, + _require: function(b) { + 1 < b.length && (b = b[1], + f.xa.test(a.V(b)) && r(b)) + }, + _providePlugin: function(a) { + if (1 < a.length) { + var d = window._hmt + , e = a[1]; + a = a[2]; + if (b.X(f.na, e) && b.j(a) && (d.plugins = d.plugins || {}, + d.z = d.z || {}, + d.plugins[e] = a, + d.l = d.l || [], + a = d.l.slice(), + e && a.length && a[0][1] === e)) + for (var g = 0, k = a.length; g < k; g++) { + var l = a[g][2] || {}; + if (d.plugins[e] && !d.z[e]) + d.z[e] = new d.plugins[e](l), + d.l.shift(); + else + break + } + } + }, + _requirePlugin: function(a) { + if (1 < a.length) { + var d = window._hmt + , e = a[1] + , g = a[2] || {}; + if (b.X(f.na, e)) + if (d.plugins = d.plugins || {}, + d.z = d.z || {}, + d.plugins[e] && !d.z[e]) + d.z[e] = new d.plugins[e](g); + else { + d.l = d.l || []; + for (var g = 0, k = d.l.length; g < k; g++) + if (d.l[g][1] === e) + return; + d.l.push(a); + n._require([u, f.xb + e + ".js"]) + } + } + } + }; + n.D(); + h.ya = n; + return h.ya + } + )(); + (function() { + var e = h.s; + c.spa !== s && "1" === String(c.spa) && (window._hmt = window._hmt || [], + window._hmt.push(["_requirePlugin", "UrlChangeTracker"]), + e.c("pv-b", function() { + "" !== window.location.hash && (h.b.a.u = window.location.href) + })) + } + )(); + (function() { + function e() { + "undefined" === typeof window["_bdhm_loaded_" + c.id] && (window["_bdhm_loaded_" + c.id] = t, + this.a = {}, + this.ob = this.ia = this.ha = t, + this.ga = m.ga, + this.Tb = k.Z(c.aet) && 0 < c.aet.length ? c.aet.split(",") : "", + this.D()) + } + var a = mt.url + , b = mt.A + , k = mt.lang + , d = mt.cookie + , f = mt.e + , g = mt.sessionStorage + , l = mt.w + , r = mt.event + , p = h.R + , n = mt.localStorage + , m = h.o + , q = h.load + , v = h.s; + e.prototype = { + Db: function() { + p.getData("HMACCOUNT") || p.setData("HMACCOUNT", c.hca) + }, + Eb: function() { + var a, b, e, f; + m.ka = p.getData("Hm_lpvt_" + c.id) || 0; + if (f = p.getData("Hm_lvt_" + c.id)) { + for (b = f.split(","); 2592E3 < m.B - b[0]; ) + b.shift(); + e = 4 > b.length ? 2 : 3; + for (m.B - m.ka > c.vdur && b.push(m.B); 4 < b.length; ) + b.shift(); + f = b.join(","); + b = b[b.length - 1] + } else + f = m.B, + b = "", + e = 1; + this.nb() ? (p.setData("Hm_lvt_" + c.id, f, c.age), + p.setData("Hm_lpvt_" + c.id, m.B), + a = d.rb(p.I(), p.U())) : this.da(); + this.a.cc = a; + this.a.lt = b; + this.a.lv = e + }, + nb: function() { + var b = a.V(document.location.href); + return !k.X("sjh.baidu.com isite.baidu.com ls.wejianzhan.com bs.wejianzhan.com product.weijianzhan.com qianhu.weijianzhan.com aisite.wejianzhan.com".split(" "), b) + }, + Aa: function() { + var a = "Hm_clear_cookie_" + c.id + , b = n.get(a) || 0; + c.fc && Number(c.fc) > Number(b) && (this.da(), + n.set(a, c.fc)) + }, + da: function() { + for (var a = document.cookie.split(";"), b = 0; b < a.length; b++) { + var d = a[b].split("="); + d.length && /Hm_(up|cv|lp?vt)_[0-9a-f]{31}/.test(String(d[0])) && p.removeData(k.trim(d[0])); + d.length && /Hm_ck_[0-9]{13}/.test(String(d[0])) && p.removeData(k.trim(d[0])) + } + }, + pa: function() { + for (var a = [], b = this.a.et, d = 0, e = m.ra.length; d < e; d++) { + var f = m.ra[d] + , g = this.a[f]; + "undefined" !== typeof g && "" !== g && ("tt" !== f || "tt" === f && 0 === b) && a.push(f + "=" + encodeURIComponent(g)) + } + return a.join("&") + }, + Fb: function() { + this.Eb(); + this.Db(); + this.a.hca = p.getData("HMACCOUNT") || ""; + this.a.si = c.id; + this.a.sn = this.ea(); + this.a.su = document.referrer; + this.a.ds = f.Ab; + this.a.cl = f.colorDepth + "-bit"; + this.a.ln = String(f.language).toLowerCase(); + this.a.ja = f.javaEnabled ? 1 : 0; + this.a.ck = f.cookieEnabled ? 1 : 0; + this.a.lo = "number" === typeof _bdhm_top ? 1 : 0; + this.a.v = "1.3.2"; + this.a.cv = decodeURIComponent(p.getData("Hm_cv_" + c.id) || ""); + this.a.tt = document.title || ""; + this.a.vl = f.W(); + var b = document.location.href; + this.a.cm = a.f(b, m.hb) || ""; + this.a.cp = a.f(b, m.ib) || a.f(b, m.Kb) || ""; + this.a.cw = a.f(b, m.gb) || a.f(b, m.Mb) || ""; + this.a.ci = a.f(b, m.eb) || a.f(b, m.Jb) || ""; + this.a.cf = a.f(b, m.jb) || a.f(b, m.Lb) || ""; + this.a.cu = a.f(b, m.fb) || a.f(b, m.Ib) || ""; + /https?:/.test(document.location.protocol) && (this.a.u = b) + }, + D: function() { + try { + this.Aa(), + this.Fb(), + this.Cb(), + h.b = this, + this.za(), + this.tb(), + v.k("pv-b"), + this.ob && this.Bb() + } catch (a) { + var d = []; + d.push("si=" + c.id); + d.push("n=" + encodeURIComponent(a.name)); + d.push("m=" + encodeURIComponent(a.message)); + d.push("r=" + encodeURIComponent(document.referrer)); + b.log(m.L + "//" + m.aa + "?" + d.join("&")) + } + }, + Bb: function() { + function a() { + v.k("pv-d") + } + this.ha ? (this.a.et = 0, + this.a.ep = "", + v.k("setPageviewProp"), + this.a.vl = f.W(), + this.m(a), + this.a.p = "") : a(); + this.sb = +new Date; + v.k("clearPageviewProp") + }, + m: function(a) { + if (this.ia) { + var d = this; + d.a.rnd = Math.round(Math.random() * m.G); + d.a.r = f.orientation; + d.a.ww = f.sa; + v.k("stag-b"); + var e = m.L + "//" + m.aa + "?" + d.pa(); + v.k("stag-d"); + d.va(e); + b.log(e, function(b) { + d.oa(b); + k.j(a) && a.call(d) + }) + } + }, + za: function() { + try { + if (window.postMessage && window.self !== window.parent) { + var b = this; + r.c(window, "message", function(d) { + if (a.V(d.origin) === m.Hb) { + d = d.data || {}; + var e = d.jn || "" + , f = /^customevent$|^heatmap$|^pageclick$|^select$/.test(e); + if (RegExp(c.id).test(d.sd || "") && f) + b.a.rnd = Math.round(Math.random() * m.G), + q(m.protocol + "//" + c.js + e + ".js?" + b.a.rnd) + } + }); + window.parent.postMessage({ + id: c.id, + url: document.location.href, + status: "__Messenger__hmLoaded" + }, "*") + } + } catch (d) {} + }, + tb: function() { + try { + if (window.self === window.parent) { + var b = document.location.href + , d = a.f(b, "baidu-analytics-token") + , e = a.f(b, "baidu-analytics-jn"); + /^[a-f0-9]{32}\/?$/.test(d) && /^(overlay|vabtest)\/?$/.test(e) && q(m.protocol + "//" + c.js + e + ".js?" + Math.round(Math.random() * m.G)) + } + } catch (f) {} + }, + va: function(a) { + var b; + try { + b = l.parse(g.get("Hm_unsent_" + c.id) || "[]") + } catch (d) { + b = [] + } + var e = this.a.u ? "" : "&u=" + encodeURIComponent(document.location.href); + b.push(a.replace(/^https?:\/\//, "") + e); + g.set("Hm_unsent_" + c.id, l.stringify(b)) + }, + oa: function(a) { + var b; + try { + b = l.parse(g.get("Hm_unsent_" + c.id) || "[]") + } catch (d) { + b = [] + } + if (b.length) { + a = a.replace(/^https?:\/\//, ""); + for (var e = 0; e < b.length; e++) + if (a.replace(/&u=[^&]*/, "") === b[e].replace(/&u=[^&]*/, "")) { + b.splice(e, 1); + break + } + b.length ? g.set("Hm_unsent_" + c.id, l.stringify(b)) : this.Ba() + } + }, + Ba: function() { + g.remove("Hm_unsent_" + c.id) + }, + Cb: function() { + var a = this, d; + try { + d = l.parse(g.get("Hm_unsent_" + c.id) || "[]") + } catch (e) { + d = [] + } + if (d.length) + for (var f = function(d) { + b.log(m.L + "//" + d, function(b) { + a.oa(b) + }) + }, k = 0; k < d.length; k++) + f(d[k]) + }, + ea: function() { + return Math.round(+new Date / 1E3) % 65535 + } + }; + return new e + } + )(); + var y = h.o + , z = h.load; + if (c.apps) { + var B = [y.protocol, "//ers.baidu.com/app/s.js?"]; + B.push(c.apps); + z(B.join("")) + } + var D = h.o + , E = h.load; + c.pt && E([D.protocol, "//ada.baidu.com/phone-tracker/insert_bdtj?sid=", c.pt].join("")); + var F = h.load; + if (c.qiao) { + for (var G = ["https://goutong.baidu.com/site/"], H = c.id, I = 5381, J = H.length, K = 0; K < J; K++) + I = (33 * I + Number(H.charCodeAt(K))) % 4294967296; + 2147483648 < I && (I -= 2147483648); + G.push(I % 1E3 + "/"); + G.push(c.id + "/b.js"); + G.push("?siteId=" + c.qiao); + F(G.join("")) + } + ; +} +)(); +未命名19:19
INFO: krpano 1.23.3 (build 2025-11-15)
INFO: Desktop - Chrome 145.0 - WebGL2
INFO: Registered to: OTHINK Technology Co.,Ltd.
CLOSE
LAYERS
HOTSPOTS
PANO
DEBUGKEYS
CLEARLOG
INPUT
Exit VR
VR Setup
创作者:Alen167人来过
全屏模式
VR模式
视角切换
未命名19:19
场景选择
简介
分享
留言
\ No newline at end of file