(function () { 'use strict'; var HISTORY_KEY = 'soondesign_history'; var VERSION = typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : '3.2.101'; function getAppBasePathname() { var p = (typeof window !== 'undefined' && window.location && window.location.pathname) ? window.location.pathname : '/'; while (p.length > 1 && p.endsWith('/')) { p = p.slice(0, -1); } if (/\.web\.html$/i.test(p) || /\.html$/i.test(p)) { p = p.replace(/[^/]+$/, ''); } if (!p.endsWith('/')) p += '/'; return p; } var SERVER_API_BASE = (function () { var cfg = (typeof window !== 'undefined' && window.SOON_DEPLOY_CONFIG && window.SOON_DEPLOY_CONFIG.api_v1_base); if (cfg) return String(cfg).replace(/\/+$/, '') + '/'; if (typeof window === 'undefined' || !window.location) return '/api/v1/'; return window.location.origin + getAppBasePathname() + 'api/v1/'; })(); function getAccessToken() { if (typeof window.soonGetAccessToken === 'function') return window.soonGetAccessToken(); try { return localStorage.getItem('soon_access') || ''; } catch (e) { return ''; } } function authedFetch(path, opts) { var url = SERVER_API_BASE + String(path).replace(/^\/+/, ''); if (typeof window.soonAuthedFetch === 'function') { return window.soonAuthedFetch(url, opts); } opts = opts || {}; opts.headers = Object.assign({}, opts.headers || {}); if (!opts.headers['Authorization'] && !opts.headers['authorization']) { var t = getAccessToken(); if (t) opts.headers['Authorization'] = 'Bearer ' + t; } return fetch(url, opts); } var JSON_HEADERS = { 'Content-Type': 'application/json; charset=utf-8', 'Accept': 'application/json' }; function parseCloudRef(p) { return typeof window.soonParseFileKey === 'function' ? window.soonParseFileKey(p) : null; } function normalizeSoonName(p) { return typeof window.soonNormalizeSoonName === 'function' ? window.soonNormalizeSoonName(p) : String(p || 'design.soon'); } function packCloudResult(j) { if (!j || !j.ok || !j.data) { return Promise.reject(new Error('cloud_empty_response')); } var fileKey = typeof window.soonApplyCloudMeta === 'function' ? window.soonApplyCloudMeta(j.data) : null; if (!fileKey) { var d = j.data; window._soonFileMeta = { id: d.id, version: d.version, name: d.name }; fileKey = 'soondesign_file:' + d.id + ':v' + d.version; } return { fileId: j.data.id, version: j.data.version, fileKey: fileKey, name: j.data.name }; } function handleCloudResponse(response, bodyText) { if (response.ok) { try { return Promise.resolve(JSON.parse(bodyText)); } catch (e) { return Promise.resolve(null); } } var info = typeof window.soonParseApiError === 'function' ? window.soonParseApiError(response, bodyText) : { status: response.status, message: '操作失败' }; if (typeof window.soonShowApiError === 'function') window.soonShowApiError(info); var err = new Error(info.message || 'cloud_error'); err.status = info.status; err.code = info.code; return Promise.reject(err); } function requireCloudAuth(actionLabel) { if (getAccessToken()) return true; if (typeof window.soonRequireLogin === 'function') return window.soonRequireLogin(actionLabel); return false; } function navigateToPage(pathWithQuery) { try { location.href = new URL(pathWithQuery, window.location.href).href; } catch (e) { location.href = pathWithQuery; } } function getLocale() { var lang = typeof navigator !== 'undefined' ? (navigator.language || navigator.browserLanguage || '') : ''; if (lang.indexOf('zh') === 0) return lang.indexOf('TW') >= 0 ? 'ozh' : 'zh'; return 'en'; } function getSystemFonts() { var list = [ 'Arial', 'Arial Black', 'Comic Sans MS', 'Courier New', 'Georgia', 'Impact', 'Microsoft YaHei', 'SimHei', 'SimSun', 'KaiTi', 'FangSong', 'Times New Roman', 'Trebuchet MS', 'Verdana', 'PingFang SC', 'Hiragino Sans GB' ]; if (typeof document !== 'undefined' && document.fonts && document.fonts.forEach) { var set = {}; document.fonts.forEach(function (f) { var name = (f.family || '').replace(/^["']|["']$/g, ''); if (name) set[name] = 1; }); list = Object.keys(set).length ? Object.keys(set).sort() : list; } return Promise.resolve(list); } function listCloudFiles(pageOrLimit, sizeOrOffset) { if (!requireCloudAuth('查看文件')) return Promise.reject(new Error('unauthorized')); var url; var fallback = { items: [], total: 0 }; if (typeof pageOrLimit === 'object' && pageOrLimit) { var page = pageOrLimit.page || 1; var size = pageOrLimit.size || 12; url = 'files?page=' + page + '&size=' + size; fallback = { items: [], total: 0, page: page, size: size }; } else { var lim = pageOrLimit || 50; var off = sizeOrOffset || 0; url = 'files?limit=' + lim + '&offset=' + off; fallback = { items: [], limit: lim, offset: off, total: 0 }; } return authedFetch(url, { headers: { Accept: 'application/json' } }) .then(function (r) { return r.text().then(function (t) { return handleCloudResponse(r, t); }); }) .then(function (j) { return (j && j.ok && j.data) ? j.data : fallback; }); } function createCloudFile(name, jsonStr) { if (!requireCloudAuth('保存')) return Promise.reject(new Error('unauthorized')); if (typeof window.soonRequireMember === 'function' && !window.soonRequireMember('保存')) { return Promise.reject(new Error('membership_required')); } return authedFetch('files', { method: 'POST', headers: JSON_HEADERS, body: JSON.stringify({ name: normalizeSoonName(name), json: jsonStr }) }) .then(function (r) { return r.text().then(function (t) { return handleCloudResponse(r, t); }); }) .then(packCloudResult); } function updateCloudFile(id, name, jsonStr, version) { if (!requireCloudAuth('保存')) return Promise.reject(new Error('unauthorized')); if (typeof window.soonRequireMember === 'function' && !window.soonRequireMember('保存')) { return Promise.reject(new Error('membership_required')); } var body = { name: normalizeSoonName(name), json: jsonStr }; if (version != null) body.version = version; return authedFetch('files/' + id, { method: 'PUT', headers: JSON_HEADERS, body: JSON.stringify(body) }) .then(function (r) { return r.text().then(function (t) { return handleCloudResponse(r, t); }); }) .then(packCloudResult); } function deleteCloudFile(id) { if (!requireCloudAuth('删除')) return Promise.reject(new Error('unauthorized')); return authedFetch('files/' + id, { method: 'DELETE', headers: { Accept: 'application/json' } }) .then(function (r) { return r.text().then(function (t) { return handleCloudResponse(r, t); }); }); } function importSoonFile(name, jsonObj) { var jsonStr = typeof jsonObj === 'string' ? jsonObj : JSON.stringify(jsonObj); return createCloudFile(name, jsonStr); } function downloadCloudFile(id, fileName) { if (!requireCloudAuth('下载')) return Promise.reject(new Error('unauthorized')); if (typeof window.soonRequireMember === 'function' && !window.soonRequireMember('下载')) { return Promise.reject(new Error('membership_required')); } return authedFetch('files/' + id + '/download', { headers: { Accept: 'application/octet-stream' } }) .then(function (response) { if (!response.ok) { return response.text().then(function (t) { return handleCloudResponse(response, t); }); } return response.blob().then(function (blob) { var a = document.createElement('a'); a.download = normalizeSoonName(fileName || 'design.soon'); a.href = URL.createObjectURL(blob); a.click(); URL.revokeObjectURL(a.href); }); }); } var bridge = { readHistory: function () { try { var raw = localStorage.getItem(HISTORY_KEY); return Promise.resolve(raw ? JSON.parse(raw) : { history: [] }); } catch (e) { return Promise.resolve({ history: [] }); } }, writeHistory: function (data) { try { localStorage.setItem(HISTORY_KEY, JSON.stringify(data)); return Promise.resolve({ success: true }); } catch (e) { return Promise.resolve({ success: false, error: e.message }); } }, listCloudFiles: listCloudFiles, createCloudFile: createCloudFile, updateCloudFile: updateCloudFile, deleteCloudFile: deleteCloudFile, importSoonFile: importSoonFile, downloadCloudFile: downloadCloudFile, requireCloudAuth: requireCloudAuth, readJsonFile: function (pathOrHandle) { if (!pathOrHandle) return Promise.resolve(null); if (typeof pathOrHandle === 'object' && pathOrHandle.text) { return pathOrHandle.text().then(function (t) { try { return JSON.parse(t); } catch (e) { return null; } }); } var key = typeof pathOrHandle === 'string' ? pathOrHandle : ''; if (key.indexOf('soondesign_session:') === 0) { try { var j = sessionStorage.getItem(key); if (!j && typeof localStorage !== 'undefined') j = localStorage.getItem(key); return Promise.resolve(j ? JSON.parse(j) : null); } catch (e) { return Promise.resolve(null); } } if (key && key.indexOf('soondesign_file:') === 0 && typeof fetch !== 'undefined') { var fileMatch = key.match(/^soondesign_file:(\d+)/); if (fileMatch && getAccessToken()) { return authedFetch('files/' + fileMatch[1] + '/download', { headers: { Accept: 'application/json' } }) .then(function (response) { if (!response.ok) return null; return response.text().then(function (text) { try { return JSON.parse(text); } catch (e) { return null; } }); }) .catch(function () { return null; }); } } return Promise.resolve(null); }, showOpenDialog: function (options) { return new Promise(function (resolve) { var input = document.createElement('input'); input.type = 'file'; if (options && options.filters && Array.isArray(options.filters) && options.filters.length > 0) { var acceptList = []; options.filters.forEach(function (filter) { if (filter.extensions && Array.isArray(filter.extensions)) { filter.extensions.forEach(function (ext) { var cleanExt = ext.replace(/^\./, ''); if (['png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp', 'svg'].indexOf(cleanExt.toLowerCase()) >= 0) { var mimeType = 'image/' + (cleanExt.toLowerCase() === 'jpg' ? 'jpeg' : cleanExt.toLowerCase()); if (acceptList.indexOf(mimeType) < 0) acceptList.push(mimeType); } else { var extWithDot = '.' + cleanExt; if (acceptList.indexOf(extWithDot) < 0) acceptList.push(extWithDot); } }); } }); input.accept = acceptList.length > 0 ? acceptList.join(',') : ''; } else { input.accept = '.soon,application/json'; } input.style.display = 'none'; input.onchange = function () { var f = input.files && input.files[0]; document.body.removeChild(input); if (!f) resolve({ canceled: true }); else resolve({ canceled: false, filePaths: [], files: [f], file: f }); }; document.body.appendChild(input); input.click(); }); }, showSaveDialog: function (options) { var raw = (options && options.defaultPath) ? options.defaultPath : ''; var defaultName = normalizeSoonName(raw || 'design.soon'); if (typeof window.soonDisplayFileName === 'function' && raw.indexOf('soondesign_file:') === 0) { defaultName = window.soonDisplayFileName(raw); } return new Promise(function (resolve) { var name = typeof prompt === 'function' ? prompt('保存为文件名(如 xxx.soon)', defaultName) : defaultName; if (name === null) { resolve({ canceled: true }); return; } var fp = (name && String(name).trim()) ? String(name).trim() : defaultName; fp = normalizeSoonName(fp); resolve({ canceled: false, filePath: fp, useCloud: true }); }); }, writeFile: function (pathOrHandle, content) { var isBlob = content instanceof Blob; var str = typeof content === 'string' ? content : (isBlob ? null : (content && content.toString ? content.toString() : '')); if (isBlob) { var blobName = typeof pathOrHandle === 'string' ? pathOrHandle : 'output.bin'; var a = document.createElement('a'); a.download = blobName; a.href = URL.createObjectURL(content); a.click(); URL.revokeObjectURL(a.href); return Promise.resolve(); } if (pathOrHandle && pathOrHandle.createWritable) { pathOrHandle = typeof pathOrHandle.name === 'string' ? pathOrHandle.name : 'design.soon'; } if (!requireCloudAuth('保存')) { return Promise.reject(new Error('unauthorized')); } var name = typeof pathOrHandle === 'string' ? pathOrHandle : 'design.soon'; var cloudRef = parseCloudRef(name); var fileName = normalizeSoonName(name); if (cloudRef && cloudRef.id) { var ver = cloudRef.version; if (ver == null && window._soonFileMeta && window._soonFileMeta.id === cloudRef.id) { ver = window._soonFileMeta.version; } return updateCloudFile(cloudRef.id, fileName, str, ver); } return createCloudFile(fileName, str); }, readFile: function (pathOrHandle) { if (pathOrHandle && pathOrHandle.getFile) { return pathOrHandle.getFile().then(function (f) { return new Promise(function (res, rej) { var r = new FileReader(); r.onload = function () { res(r.result); }; r.onerror = rej; r.readAsArrayBuffer(f); }); }); } if (pathOrHandle && pathOrHandle.arrayBuffer) { return pathOrHandle.arrayBuffer(); } return Promise.reject(new Error('No file')); }, getAppVersion: function () { return Promise.resolve(VERSION); }, getLocale: getLocale, getUserDataPath: function () { return ''; }, getSystemFonts: getSystemFonts, openDesignPage: function (file, type) { var t = type || 1; if (file && typeof sessionStorage !== 'undefined') { try { sessionStorage.setItem('soondesign_open_file', file); sessionStorage.setItem('soondesign_open_type', String(t)); if (file.indexOf('soondesign_file:') === 0 && window._soonFileMeta) { sessionStorage.setItem('soondesign_open_meta', JSON.stringify(window._soonFileMeta)); } } catch (e) {} } var fileParam = file ? encodeURIComponent(file) : ''; navigateToPage('design' + t + '.web.html?file=' + fileParam + '&type=' + t); }, openFirstPage: function () { navigateToPage('index.web.html'); }, onClose: function (callback) { if (typeof window !== 'undefined') { window.addEventListener('beforeunload', callback); } }, runClose: function () { if (typeof window !== 'undefined' && window.close) window.close(); }, openHelp: function () { try { window.open(new URL('../assets/help/User Manual.pdf', window.location.href).href, '_blank'); } catch (e) { window.open('../assets/help/User Manual.pdf', '_blank'); } }, printPdf: function (urlOrBlob) { var url = urlOrBlob; if (urlOrBlob && typeof urlOrBlob === 'object' && !(urlOrBlob instanceof String)) { url = URL.createObjectURL(urlOrBlob); } var w = window.open(url, '_blank'); if (w) w.onload = function () { w.print(); }; }, getScaleRate: function () { return typeof window !== 'undefined' && window.devicePixelRatio ? window.devicePixelRatio : 1; }, clipboard: { readText: function () { return navigator.clipboard && navigator.clipboard.readText ? navigator.clipboard.readText() : Promise.resolve(''); }, writeText: function (text) { if (navigator.clipboard && navigator.clipboard.writeText) { return navigator.clipboard.writeText(text); } return Promise.resolve(); } } }; window.platformBridge = bridge; var pathStub = { join: function () { return [].slice.call(arguments).join('/').replace(/\/+/g, '/'); } }; window.sysAPI = { readHistory: bridge.readHistory, writeHistory: bridge.writeHistory, readJsonFile: bridge.readJsonFile, getAppVersion: bridge.getAppVersion }; window.dialog = { showOpenDialog: bridge.showOpenDialog, showSaveDialog: bridge.showSaveDialog }; window.path = pathStub; window.fs = null; window.remote = null; var loc = getLocale(); window.ipcRenderer = { send: function (ch, a1, a2) { if (ch === 'get-sys-language') { window._sysLanPending = true; setTimeout(function () { if (window._sysLanCb && window._sysLanPending) { window._sysLanCb(null, loc); window._sysLanPending = false; } }, 0); } if (ch === 'open-first-page' && bridge.openFirstPage) { bridge.openFirstPage(); } if (ch === 'open-design-page' && bridge.openDesignPage && (a1 !== undefined || a2 !== undefined)) { bridge.openDesignPage(a1 || '', a2 || 1); } if (ch === 'open-help-file' && bridge.openHelp) { bridge.openHelp(); } if (ch === 'run-close' && bridge.runClose) { bridge.runClose(); } }, on: function (ch, cb) { if (ch === 'sys-lan') { window._sysLanCb = cb; if (window._sysLanPending) setTimeout(function () { if (window._sysLanCb) { window._sysLanCb(null, loc); window._sysLanPending = false; } }, 0); } if (ch === 'close') window._closeCb = cb; } }; window.clipboard = bridge.clipboard; window.exePath = ''; window.fullPath = ''; })();