@quick-game/cli
Version:
Command line interface for rapid qg development
1,417 lines (1,357 loc) • 56.4 kB
JavaScript
(function () {
// #region 通用变量和方法
let isOPPO = () => {
const result = window.qg.getProvider().toLowerCase() === 'oppo'
isOPPO = () => result
return isOPPO()
}
let isVivo = () => {
const result = window.qg.getProvider().toLowerCase() === 'vivo'
isVivo = () => result
return isVivo()
}
let isMi = () => {
const result = window.qg.getProvider().toLowerCase().includes('mi')
isMi = () => result
return isMi()
}
let isHonor = () => {
const result = window.qg.getProvider().toLowerCase() === 'honor'
isHonor = () => result
return isHonor()
}
// #endregion
// ### 底层函数 Object 相关修改 ###
// 保存原始的 Object.defineProperty 方法
var originalDefineProperty = Object.defineProperty
// 重定义 Object.defineProperty 方法
Object.defineProperty = function (obj, prop, descriptor) {
if (prop === 'HTMLCanvasElement') {
try {
// 调用原始的 Object.defineProperty 方法
return originalDefineProperty(obj, prop, descriptor)
} catch (err) {
console.warn(err)
return obj
}
} else {
// 调用原始的 Object.defineProperty 方法
return originalDefineProperty(obj, prop, descriptor)
}
}
// 将对象定义为不可更改
const defineReadOnlyPropertyContain = (obj, name, value) => {
try {
Object.defineProperty(obj, name, {
enumerable: true,
get () {
return value
},
set (newValue) {
if (newValue !== value) {
console.warn(`rewrite the property: ${name}, read only.`, newValue)
return
}
value = newValue
}
})
} catch (e) {
console.warn('不影响正常运行:', e)
}
}
// 将对象定义为不可更改
const defineReadOnlyPropertyCanDefine = (obj, name, value) => {
try {
Object.defineProperty(obj, name, {
enumerable: true,
get () {
return value
},
set (newValue) {
if (newValue !== value) {
console.warn(`rewrite the property: ${name}`, newValue)
}
value = newValue
}
})
} catch (e) {
console.warn('不影响正常运行:', e)
}
}
try {
// 兼容vivo旧的readFileSync接口的回调接口
const stringPro = String.prototype
Object.defineProperty(stringPro, 'data', {
get: function () {
return this.toString()
},
enumerable: false
})
Object.defineProperty(stringPro, 'text', {
get: function () {
return this.toString()
},
enumerable: false
})
if (!('replaceAll' in stringPro)) {
Object.defineProperty(stringPro, 'replaceAll', {
get: function (from, to) {
from = from.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
return this.replace(new RegExp(from, 'g'), to)
},
enumerable: false
})
}
const arrayBufferPro = ArrayBuffer.prototype
Object.defineProperty(arrayBufferPro, 'data', {
get: function () {
return this
},
enumerable: false
})
Object.defineProperty(arrayBufferPro, 'text', {
get: function () {
return this
},
enumerable: false
})
if (!('replaceAll' in stringPro)) {
Object.defineProperty(stringPro, 'replaceAll', {
get: function (from, to) {
from = from.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
return this.replace(new RegExp(from, 'g'), to)
},
enumerable: false
})
}
} catch (err) {
}
// ### 渲染层相关修改 ###
let descriptor = Object.getOwnPropertyDescriptor(window, 'TouchEvent')
if (window.TouchEvent && descriptor && descriptor.writable && descriptor.configurable) {
defineReadOnlyPropertyContain(window, 'TouchEvent', window.TouchEvent)
}
descriptor = Object.getOwnPropertyDescriptor(window, 'Image')
if (window.Image && descriptor && descriptor.writable && descriptor.configurable) {
defineReadOnlyPropertyContain(window, 'Image', window.Image)
}
descriptor = Object.getOwnPropertyDescriptor(window, 'HTMLImageElement')
if (window.HTMLImageElement && descriptor && descriptor.writable && descriptor.configurable) {
defineReadOnlyPropertyContain(window, 'HTMLImageElement', window.HTMLImageElement)
}
if (!window.qg.createImage && window.Image) {
defineReadOnlyPropertyContain(window.qg, 'createImage', () => {
return new window.Image()
})
}
descriptor = Object.getOwnPropertyDescriptor(window, 'Blob')
if (window.Blob && descriptor && descriptor.writable && descriptor.configurable) {
defineReadOnlyPropertyContain(window, 'Blob', window.Blob)
}
descriptor = Object.getOwnPropertyDescriptor(window, 'URL')
if (window.URL && descriptor && descriptor.writable && descriptor.configurable) {
defineReadOnlyPropertyContain(window, 'URL', window.URL)
}
// 兼容 __cccanvas __canvas, cocos低版本会使用到
// OPPO是__canvas,小米是canvas
if (!isVivo()) {
if (!window.canvas) {
// 荣耀平台qg.createCanvas 无法获取gl,需要通过HTMLCanvasElement获取,而且只要调用一次webgl,否则会失败
if (window.HTMLCanvasElement && !window.__canvas && isHonor()) {
const canvas = new window.HTMLCanvasElement()
if (canvas.getContext && canvas.toDataURL) {
window.canvas = canvas
}
}
if (!window.canvas) {
window.canvas = window.__canvas ? window.__canvas : window.qg.createCanvas()
}
}
}
if (!window.mainCanvas) {
window.mainCanvas = window.canvas ? window.canvas : window.qg.createCanvas()
}
if (!window.__cccanvas) {
window.__cccanvas = window.canvas ? window.canvas : window.qg.createCanvas()
}
if (!window.__canvas) {
window.__canvas = window.canvas ? window.canvas : window.qg.createCanvas()
}
// 针对unity runtime,小米的canvas只能获取2d或者webgl,如果先获取2d会导致webgl获取失败,所以这里先获取webgl,保证后续可以正常渲染上屏
if (window.mainCanvas && window.__miEngineType === 'unity' && isMi()) {
window.mainCanvas.getContext('webgl')
}
descriptor = Object.getOwnPropertyDescriptor(window, 'HTMLCanvasElement')
if (window.HTMLCanvasElement && descriptor && descriptor.writable && descriptor.configurable) {
const canvas = new window.HTMLCanvasElement()
if (canvas.getContext && canvas.toDataURL) {
let originCanvas = window.HTMLCanvasElement
Object.defineProperty(window, 'HTMLCanvasElement', {
enumerable: true,
configurable: false,
get () {
return originCanvas
},
set (newValue) {
if (newValue !== originCanvas) {
console.warn(`rewrite the property: HTMLCanvasElement, read only.`, newValue)
return
}
originCanvas = newValue
}
})
// 兼容 qg.createCanvas().constructor 会导致荣耀崩溃
if (isHonor()) {
descriptor = Object.getOwnPropertyDescriptor(window.qg, 'createCanvas')
if (window.qg.createCanvas && descriptor && descriptor.writable && descriptor.configurable) {
const originalCreateCanvas = window.qg.createCanvas
defineReadOnlyPropertyContain(window.qg, 'createCanvas', (params) => {
const canvasObject = originalCreateCanvas.apply(this, params)
// Modify the constructor property of the returned object
Object.defineProperty(canvasObject, 'constructor', {
value: window.HTMLCanvasElement,
enumerable: false,
writable: true,
configurable: true
})
return canvasObject
})
}
}
} else {
Object.defineProperty(window, 'HTMLCanvasElement', {
set: function set (val) { },
get: function get () {
return window.__canvas.constructor
}
})
// 兼容其他平台 getContext只能获取一个,先获取2d会导致webgl获取失败,所以这里先获取webgl,保证后续可以正常渲染上屏
descriptor = Object.getOwnPropertyDescriptor(window.mainCanvas, 'getContext')
if (window.mainCanvas.getContext && descriptor && descriptor.writable && descriptor.configurable) {
const originalGetContext = window.mainCanvas.getContext
defineReadOnlyPropertyContain(window.mainCanvas, 'getContext', (params) => {
var contextObject = null
try {
contextObject = originalGetContext.apply(window.mainCanvas, Array.isArray(params) ? params : [params])
} catch (err) {
console.error(err)
}
if (!contextObject) {
try {
contextObject = new window.HTMLCanvasElement().getContext(params)
} catch (err) {
console.error(err)
}
}
return contextObject
})
}
}
}
// ##引擎插件适配,联盟暂时不支持引擎插件
try {
descriptor = window.requirePlugin && Object.getOwnPropertyDescriptor(window, 'requirePlugin')
if (!window.requirePlugin || (descriptor && descriptor.writable && descriptor.configurable)) {
const requirePluginOrigin = window.requirePlugin
defineReadOnlyPropertyCanDefine(window, 'requirePlugin', (params) => {
try {
if (requirePluginOrigin && window.qg.getProvider().toLowerCase() === 'vivo') {
return requirePluginOrigin.apply(this, params)
}
} catch (err) {
console.error(err)
}
return window.require(params)
})
}
} catch (err) { }
// ### cocos或laya特殊适配层逻辑转换 ###
// 兼容 loadRuntime
if (!window.loadRuntime) {
window.loadRuntime = () => {
return window.qg
}
}
// 兼容 __gl
if (window.WebGLRenderingContext && !window.__gl) {
window.__gl = window.WebGLRenderingContext.prototype
}
// 兼容 getCanvasRenderingContext2D
if (window.CanvasRenderingContext2D && !window.qg.getCanvasRenderingContext2D) {
defineReadOnlyPropertyContain(window.qg, 'getCanvasRenderingContext2D', () => {
return window.CanvasRenderingContext2D
})
}
// 给cocos 游戏使用 兼容适配
window.scriptEngineType = ''
if (!window.__getOS) {
defineReadOnlyPropertyContain(window, '__getOS', () => 'Android')
}
if (!window.__getCurrentLanguage) {
defineReadOnlyPropertyContain(window, '__getCurrentLanguage', () => 'zh')
}
if (!window.__getOSVersion) {
defineReadOnlyPropertyContain(window, '__getOSVersion', () => 'android')
}
if (!window.__getPlatform) {
defineReadOnlyPropertyContain(window, '__getPlatform', () => 3) // cocos 专用,写死的3
}
// 兼容 getUserAgent
if (!window.qg.getUserAgent) {
defineReadOnlyPropertyContain(window.qg, 'getUserAgent', () => {
return 'Dalvik/2.1.0 (Linux; U; Build/RP1A.200720.012) hap/vivo com.vivo.hybrid/ VIVO VVGame '
})
}
// 兼容laya引擎获取userAgent判断逻辑修改
if (window.navigator) {
window.navigator.userAgent = 'Dalvik/2.1.0 (Linux; U; Build/RP1A.200720.012) hap/vivo com.vivo.hybrid/ VIVO VVGame NetType/WIFI Language/zh_CN'
} else {
window.navigator = {}
window.navigator.userAgent = 'Dalvik/2.1.0 (Linux; U; Build/RP1A.200720.012) hap/vivo com.vivo.hybrid/ VIVO VVGame NetType/WIFI Language/zh_CN'
}
// 兼容 getCurrentLanguage
if (!window.qg.getCurrentLanguage) {
defineReadOnlyPropertyContain(window.qg, 'getCurrentLanguage', () => {
return 'zh'
})
}
// 兼容 getOS
if (!window.qg.getOS) {
defineReadOnlyPropertyContain(window.qg, 'getOS', () => {
return 'android'
})
}
// 兼容jsb接口
const systemInfo = window.qg.getSystemInfoSync()
if (!window.jsb) {
window.jsb = {}
}
if (!window.jsb.openURL) {
window.jsb.openURL = () => { }
}
if (!window.jsb.AudioEngine) {
window.jsb.AudioEngine = window.qg.createInnerAudioContext()
}
if (!window.jsb.loadFont) {
window.jsb.loadFont = window.qg.loadFont
}
if (!window.jsb.setPreferredFramesPerSecond) {
window.jsb.setPreferredFramesPerSecond = window.qg.setPreferredFramesPerSecond
}
if (!window.jsb.device) {
window.jsb.device = {
setMotionInterval: () => { },
setMotionEnabled: () => { },
dispatchDeviceMotionEvent: () => { }
}
}
if (!window.jsb.reflection) {
window.jsb.reflection = {
callStaticMethod: function () {
console.error('不支持jsb.reflection.callStaticMethod这种调用,请尽快删除这种调用')
}
}
}
if (!window.jsb.language) {
window.jsb.language = systemInfo.language
}
if (!window.jsb.system) {
window.jsb.system = systemInfo.system
}
if (!window.jsb.platform) {
defineReadOnlyPropertyContain(window.jsb, 'platform', 'android')
}
// 字体相关baseLine适配到web标准,cocos3.6+才支持兼容逻辑
if (!window.qg.getFeature) {
defineReadOnlyPropertyContain(window.qg, 'getFeature', (name) => {
if (name === 'canvas.context2d.textbaseline.alphabetic' || name === 'canvas.context2d.textbaseline.default') {
return 1
}
})
}
if (!window.qg.setFeature) {
defineReadOnlyPropertyContain(window.qg, 'setFeature', () => { })
}
var _temp_downloadFile = window.qg.downloadFile
var _raw_qg = window.qg
// 兼容下载文件时,文件夹不存在时,先创建文件夹再下载
function _downloadFile() {
var data = arguments[0];
if (window.qg && _temp_downloadFile && data && data.filePath) {
var fs = window.qg.getFileSystemManager();
var dirPath = data.filePath.substring(0, data.filePath.lastIndexOf('/'));
var __state = false
try {
fs.accessSync(dirPath)
// 目录存在,直接下载
_temp_downloadFile(data);
__state = true
} catch (error) {
console.log(error);
__state=false
}
if (!__state) {
try {
//创建后下载
fs.mkdirSync(dirPath, true);
console.log("fs.mkdirSync successful!!!");
_temp_downloadFile(data);
} catch (error) {
console.log(error);
_temp_downloadFile(data);
}
}
}else{
_temp_downloadFile(data)//存在qg.downloadFile方法,但未含有filePath参数
}
}
if (isOPPO()) {
window.qg = new Proxy(_raw_qg, {
get(target, prop, receiver) {
if (prop === 'downloadFile') {
return _downloadFile
}
return Reflect.get(target, prop, receiver)
}
})
}
// ### API1.0到2.0联盟接口的适配层 ###
// ###### Network ######
if (!window.qg.download && window.qg.downloadFile) {
defineReadOnlyPropertyContain(window.qg, 'download', window.qg.downloadFile)
}
// ###### Storage ######
// 兼容 getStorageSync 旧接口
descriptor = Object.getOwnPropertyDescriptor(window.qg, 'getStorageSync')
if (window.qg.getStorageSync && descriptor && descriptor.writable && descriptor.configurable) {
const getStorageSyncOrigin = window.qg.getStorageSync
defineReadOnlyPropertyContain(window.qg, 'getStorageSync', (data) => {
if (data && typeof data !== 'string' && data.key) {
try {
return getStorageSyncOrigin(data.key)
} catch (err) { }
return ''
}
return getStorageSyncOrigin(data)
})
}
// 兼容 deleteStorageSync
descriptor = Object.getOwnPropertyDescriptor(window.qg, 'deleteStorageSync')
if (window.qg.deleteStorageSync && descriptor && descriptor.writable && descriptor.configurable) {
const deleteStorageSyncOrigin = window.qg.deleteStorageSync
defineReadOnlyPropertyContain(window.qg, 'deleteStorageSync', (data) => {
if (data && typeof data !== 'string' && data.key) {
try {
return deleteStorageSyncOrigin(data.key)
} catch (err) { }
return
}
return deleteStorageSyncOrigin(data)
})
} if (window.qg.removeStorageSync && !window.qg.deleteStorageSync && descriptor && descriptor.writable && descriptor.configurable) {
defineReadOnlyPropertyContain(window.qg, 'deleteStorageSync', (data) => {
if (data && typeof data !== 'string' && data.key) {
try {
return window.qg.removeStorageSync(data.key)
} catch (err) { }
return
}
return window.qg.removeStorageSync(data)
})
}
descriptor = Object.getOwnPropertyDescriptor(window.qg, 'deleteStorage')
if (window.qg.removeStorage && !window.qg.deleteStorage && descriptor && descriptor.writable && descriptor.configurable) {
defineReadOnlyPropertyContain(window.qg, 'deleteStorage', window.qg.removeStorage)
}
// 兼容 setStorageSync
descriptor = Object.getOwnPropertyDescriptor(window.qg, 'setStorageSync')
if (window.qg.setStorageSync && descriptor && descriptor.writable && descriptor.configurable) {
const setStorageSyncOrigin = window.qg.setStorageSync
defineReadOnlyPropertyContain(window.qg, 'setStorageSync', (...params) => {
let data
let storageData = {}
if (params.length === 1) {
data = params[0].value || params[0].data
storageData = {
key: params[0].key || '',
value: data || ''
}
try {
return setStorageSyncOrigin(storageData.key, storageData.value)
} catch (err) { }
return 'key not define'
} else {
storageData = {
key: params[0] || '',
value: params[1] || ''
}
return setStorageSyncOrigin(storageData.key, storageData.value)
}
})
}
// ###### File ######
var fs = window.qg.getFileSystemManager()
var needUserReplace = false
if (window.qg.env && window.qg.env.USER_DATA_PATH !== 'internal://files') {
console.warn(`qg.env.USER_DATA_PATH is not alliance, ${window.qg.env.USER_DATA_PATH}`)
needUserReplace = true
}
// 兼容 qg 旧的文件管理
if (!window.qg.accessFile) {
defineReadOnlyPropertyContain(window.qg, 'accessFile', (data) => {
try {
fs.accessSync(needUserReplace ? data.uri.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.uri)
return 'true'
} catch (err) { }
return 'false'
})
}
if (!window.qg.copyFile) {
defineReadOnlyPropertyContain(window.qg, 'copyFile', (data) => {
if (data.srcUri) {
data.srcPath = data.srcUri
}
data.srcPath = needUserReplace ? data.srcPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.srcPath
if (data.dstUri) {
data.destPath = data.dstUri
}
data.destPath = needUserReplace ? data.destPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.destPath
return fs.copyFile(data)
})
}
if (!window.qg.moveFile) {
defineReadOnlyPropertyContain(window.qg, 'moveFile', (data) => {
if (data.srcUri) {
data.oldPath = data.srcUri
}
data.oldPath = needUserReplace ? data.oldPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.oldPath
if (data.dstUri) {
data.newPath = data.dstUri
}
data.newPath = needUserReplace ? data.newPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.newPath
return fs.rename(data)
})
}
if (!window.qg.getFileInfo) {
defineReadOnlyPropertyContain(window.qg, 'getFileInfo', (data) => {
if (data.uri) {
data.filePath = data.uri
}
data.filePath = needUserReplace ? data.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.filePath
const suc = data.success
data.success = (res) => {
res.uri = data.filePath
res.length = res.size
res.lastModifiedTime = 0
if (suc && typeof suc === 'function') {
suc(res)
}
}
return fs.getFileInfo(data)
})
}
if (!window.qg.listDir) {
defineReadOnlyPropertyContain(window.qg, 'listDir', (data) => {
if (data.uri) {
data.path = data.uri
}
data.path = needUserReplace ? data.path.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.path
data.recursive = true
const suc = data.success
data.success = (res) => {
var files = []
for (var i = 0; i < res.length; i++) {
const file = {
uri: res[i].path,
length: res[i].stats.size,
lastModifiedTime: res[i].stats.lastModifiedTime
}
files.push(file)
}
if (suc && typeof suc === 'function') {
suc({
fileList: files
})
}
}
return fs.stat(data)
})
}
if (!window.qg.deleteFile) {
defineReadOnlyPropertyContain(window.qg, 'deleteFile', (data) => {
if (data.uri) {
data.filePath = data.uri
}
data.filePath = needUserReplace ? data.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.filePath
return fs.unlink(data)
})
}
if (!window.qg.readFile) {
defineReadOnlyPropertyContain(window.qg, 'readFile', (data) => {
if (data.uri) {
data.filePath = data.uri
}
data.filePath = needUserReplace ? data.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.filePath
const suc = data.success
data.success = (res) => {
res.text = res.data
if (suc && typeof suc === 'function') {
suc(res)
}
}
return fs.readFile(data)
})
}
if (!window.qg.readFileSync) {
defineReadOnlyPropertyContain(window.qg, 'readFileSync', (data) => {
try {
let res
const filePath = needUserReplace ? data.uri.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.uri
if (data.encoding && data.position && data.length) {
res = fs.readFileSync(filePath, data.encoding, data.position, data.length)
} else if (data.encoding && data.position) {
res = fs.readFileSync(filePath, data.encoding, data.position)
} else if (data.encoding) {
res = fs.readFileSync(filePath, data.encoding)
} else {
res = fs.readFileSync(filePath)
}
if (res) {
return {
text: res
}
}
} catch (err) {
console.error(err)
}
return 'no such file or directory'
})
}
if (!window.qg.writeFile) {
defineReadOnlyPropertyContain(window.qg, 'writeFile', (data) => {
if (data.uri) {
data.filePath = data.uri
}
data.filePath = needUserReplace ? data.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.filePath
if (data.text) {
data.data = data.text
}
return fs.writeFile(data)
})
}
if (!window.qg.writeFileSync) {
defineReadOnlyPropertyContain(window.qg, 'writeFileSync', (data) => {
try {
const filePath = needUserReplace ? data.uri.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.uri
if (data.text && data.encoding) {
return fs.writeFileSync(filePath, data.text, data.encoding)
} else {
return fs.writeFileSync(filePath, data.text)
}
} catch (err) {
console.error(err)
}
return 'no such file or directory'
})
}
if (!window.qg.appendFile) {
defineReadOnlyPropertyContain(window.qg, 'appendFile', (data) => {
if (data.uri) {
data.filePath = data.uri
}
data.filePath = needUserReplace ? data.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.filePath
if (data.text) {
data.data = data.text
}
return fs.appendFile(data)
})
}
if (!window.qg.mkdir) {
defineReadOnlyPropertyContain(window.qg, 'mkdir', (data) => {
if (data.uri) {
data.dirPath = data.uri + '/'
data.recursive = true
}
data.dirPath = needUserReplace ? data.dirPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.dirPath
return fs.mkdir(data)
})
}
if (!window.qg.rmdir) {
defineReadOnlyPropertyContain(window.qg, 'rmdir', (data) => {
if (data.uri) {
data.dirPath = data.uri
}
data.dirPath = needUserReplace ? data.dirPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.dirPath
return fs.rmdir(data)
})
}
if (!window.qg.isDirectory) {
defineReadOnlyPropertyContain(window.qg, 'isDirectory', (data) => {
try {
const res = fs.statSync(needUserReplace ? data.uri.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.uri, false)
return res.isDirectory()
} catch (err) { }
return false
})
}
if (!window.qg.isFile) {
defineReadOnlyPropertyContain(window.qg, 'isFile', (data) => {
try {
const res = fs.statSync(needUserReplace ? data.uri.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.uri, false)
return res.isFile()
} catch (err) { }
return false
})
}
if (!window.qg.renameFile) {
defineReadOnlyPropertyContain(window.qg, 'renameFile', (data) => {
if (data.srcUri) {
data.oldPath = data.srcUri
}
data.oldPath = needUserReplace ? data.oldPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.oldPath
if (data.dstUri) {
data.newPath = data.dstUri
}
data.newPath = needUserReplace ? data.newPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.newPath
return fs.rename(data)
})
}
if (!window.qg.unzipFile) {
defineReadOnlyPropertyContain(window.qg, 'unzipFile', (data) => {
if (data.srcUri) {
data.zipFilePath = data.srcUri
}
data.zipFilePath = needUserReplace ? data.zipFilePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.zipFilePath
if (data.dstUri) {
data.targetPath = data.dstUri
}
data.targetPath = needUserReplace ? data.targetPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : data.targetPath
return fs.unzip(data)
})
}
// 兼容 FileSystemManager
const readInject = fs.readFileSync
defineReadOnlyPropertyCanDefine(fs, 'readFileSync', (...params) => {
if (params.length === 1 && (params[0].uri || params[0].filePath)) {
try {
let filePath = params[0].uri || params[0].filePath
filePath = needUserReplace ? filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : filePath
if (params[0].encoding && params[0].position && params[0].length) {
const result = readInject.bind(fs)(filePath, params[0].encoding, params[0].position, params[0].length)
return result
} else if (params[0].encoding && params[0].position) {
const result = readInject.bind(fs)(filePath, params[0].encoding, params[0].position)
return result
} else if (params[0].encoding) {
const result = readInject.bind(fs)(filePath, params[0].encoding)
return result
} else {
const result = readInject.bind(fs)(filePath)
return result
}
} catch (err) {
console.error(err)
throw (err)
}
}
params[0] = needUserReplace ? params[0].replace('internal://files', window.qg.env.USER_DATA_PATH) : params[0]
return readInject.bind(fs)(...params)
})
const unlinkInject = fs.unlink
defineReadOnlyPropertyCanDefine(fs, 'unlink', (params) => {
params.filePath = needUserReplace ? params.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.filePath
return unlinkInject.bind(fs)(params)
})
const copyFileInject = fs.copyFile
defineReadOnlyPropertyCanDefine(fs, 'copyFile', (params) => {
params.srcPath = needUserReplace ? params.srcPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.srcPath
params.destPath = needUserReplace ? params.destPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.destPath
return copyFileInject.bind(fs)(params)
})
const writeFileInject = fs.writeFile
defineReadOnlyPropertyCanDefine(fs, 'writeFile', (params) => {
params.filePath = needUserReplace ? params.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.filePath
if (ArrayBuffer.isView(params.data) || params.data instanceof ArrayBuffer) {
params.encoding = 'binary'
}
return writeFileInject.bind(fs)(params)
})
const writeFileSyncInject = fs.writeFileSync
defineReadOnlyPropertyCanDefine(fs, 'writeFileSync', (path, data, encoding) => {
path = needUserReplace ? path.replace('internal://files', window.qg.env.USER_DATA_PATH) : path
if (ArrayBuffer.isView(data) || data instanceof ArrayBuffer) {
return writeFileSyncInject.bind(fs)(path, data, 'binary')
}
return writeFileSyncInject.bind(fs)(path, data, encoding)
})
const readFileInject = fs.readFile
defineReadOnlyPropertyCanDefine(fs, 'readFile', (params) => {
params.filePath = needUserReplace ? params.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.filePath
params.encoding = params.encoding || 'binary'
// @note: OPPO 底层在路径不存在时未回调 fail,在此兼容一下,其他渠道保持不变
if (isOPPO()) {
try {
fs.accessSync(params.filePath)
return readFileInject.bind(fs)(params)
} catch (err) {
if (params.fail && typeof params.fail === 'function') {
params.fail(err)
}
if (params.complete && typeof params.complete === 'function') {
params.complete(err)
}
}
} else {
return readFileInject.bind(fs)(params)
}
})
const readdirInject = fs.readdir
defineReadOnlyPropertyCanDefine(fs, 'readdir', (params) => {
params.dirPath = needUserReplace ? params.dirPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.dirPath
return readdirInject.bind(fs)(params)
})
const mkdirSyncInject = fs.mkdirSync
defineReadOnlyPropertyCanDefine(fs, 'mkdirSync', (path, recursive) => {
path = needUserReplace ? path.replace('internal://files', window.qg.env.USER_DATA_PATH) : path
return mkdirSyncInject.bind(fs)(path, recursive)
})
const rmdirSyncInject = fs.rmdirSync
defineReadOnlyPropertyCanDefine(fs, 'rmdirSync', (dirPath, recursive) => {
dirPath = needUserReplace ? dirPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : dirPath
return rmdirSyncInject.bind(fs)(dirPath, recursive)
})
const accessInject = fs.access
defineReadOnlyPropertyCanDefine(fs, 'access', (params) => {
params.path = needUserReplace ? params.path.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.path
return accessInject.bind(fs)(params)
})
const accessSyncInject = fs.accessSync
defineReadOnlyPropertyCanDefine(fs, 'accessSync', (filePath) => {
filePath = needUserReplace ? filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : filePath
return accessSyncInject.bind(fs)(filePath)
})
const unzipInject = fs.unzip
defineReadOnlyPropertyCanDefine(fs, 'unzip', (params) => {
params.zipFilePath = needUserReplace ? params.zipFilePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.zipFilePath
params.targetPath = needUserReplace ? params.targetPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.targetPath
return unzipInject.bind(fs)(params)
})
if (isHonor()) {
const saveFileInject = fs.saveFile
defineReadOnlyPropertyCanDefine(fs, 'saveFile', (params) => {
params.filePath = needUserReplace ? params.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.filePath
return saveFileInject.bind(fs)(params)
})
const saveFileSyncInject = fs.saveFileSync
defineReadOnlyPropertyCanDefine(fs, 'saveFileSync', (tempFilePath, filePath) => {
filePath = needUserReplace ? filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : filePath
return saveFileSyncInject.bind(fs)(tempFilePath, filePath)
})
const mkdirInject = fs.mkdir
defineReadOnlyPropertyCanDefine(fs, 'mkdir', (params) => {
params.dirPath = needUserReplace ? params.dirPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.dirPath
return mkdirInject.bind(fs)(params)
})
const readdirSyncInject = fs.readdirSync
defineReadOnlyPropertyCanDefine(fs, 'readdirSync', (dirPath) => {
dirPath = needUserReplace ? dirPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : dirPath
return readdirSyncInject.bind(fs)(dirPath)
})
const copyFileSyncInject = fs.copyFileSync
defineReadOnlyPropertyCanDefine(fs, 'copyFileSync', (srcPath, destPath) => {
srcPath = needUserReplace ? srcPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : srcPath
destPath = needUserReplace ? destPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : destPath
return copyFileSyncInject.bind(fs)(srcPath, destPath)
})
const getFileInfoInject = fs.getFileInfo
defineReadOnlyPropertyCanDefine(fs, 'getFileInfo', (params) => {
params.filePath = needUserReplace ? params.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.filePath
return getFileInfoInject.bind(fs)(params)
})
const rmdirInject = fs.rmdir
defineReadOnlyPropertyCanDefine(fs, 'rmdir', (params) => {
params.dirPath = needUserReplace ? params.dirPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.dirPath
return rmdirInject.bind(fs)(params)
})
const unlinkSyncInject = fs.unlinkSync
defineReadOnlyPropertyCanDefine(fs, 'unlinkSync', (filePath) => {
filePath = needUserReplace ? filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : filePath
return unlinkSyncInject.bind(fs)(filePath)
})
const appendFileInject = fs.appendFile
defineReadOnlyPropertyCanDefine(fs, 'appendFile', (params) => {
params.filePath = needUserReplace ? params.filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.filePath
return appendFileInject.bind(fs)(params)
})
const appendFileSyncInject = fs.appendFileSync
defineReadOnlyPropertyCanDefine(fs, 'appendFileSync', (filePath, data, encoding) => {
filePath = needUserReplace ? filePath.replace('internal://files', window.qg.env.USER_DATA_PATH) : filePath
return appendFileSyncInject.bind(fs)(filePath, data, encoding)
})
const renameInject = fs.rename
defineReadOnlyPropertyCanDefine(fs, 'rename', (params) => {
params.newPath = needUserReplace ? params.newPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.newPath
params.oldPath = needUserReplace ? params.oldPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.oldPath
return renameInject.bind(fs)(params)
})
const renameSyncInject = fs.renameSync
defineReadOnlyPropertyCanDefine(fs, 'renameSync', (oldPath, newPath) => {
oldPath = needUserReplace ? oldPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : oldPath
newPath = needUserReplace ? newPath.replace('internal://files', window.qg.env.USER_DATA_PATH) : newPath
return renameSyncInject.bind(fs)(oldPath, newPath)
})
const statInject = fs.stat
defineReadOnlyPropertyCanDefine(fs, 'stat', (params) => {
params.path = needUserReplace ? params.path.replace('internal://files', window.qg.env.USER_DATA_PATH) : params.path
return statInject.bind(fs)(params)
})
const statSyncInject = fs.statSync
defineReadOnlyPropertyCanDefine(fs, 'statSync', (path, recursive) => {
path = needUserReplace ? path.replace('internal://files', window.qg.env.USER_DATA_PATH) : path
return statSyncInject.bind(fs)(path, recursive)
})
}
// 兼容 subscribeNetworkStatus
if (window.qg.onNetworkStatusChange && !window.qg.subscribeNetworkStatus) {
defineReadOnlyPropertyContain(window.qg, 'subscribeNetworkStatus', (params = {}) => {
const suc = params.success
params.success = (data) => {
data.type = data.networkType
if (suc && typeof suc === 'function') {
suc(data)
}
}
return window.qg.onNetworkStatusChange(params.success)
})
}
// 兼容 loadFont
descriptor = Object.getOwnPropertyDescriptor(window.qg, 'loadFont')
if (window.qg.loadFont && descriptor && descriptor.writable && descriptor.configurable) {
const loadFontOrigin = window.qg.loadFont
defineReadOnlyPropertyContain(window.qg, 'loadFont', (...params) => {
if (params.length === 1) {
return loadFontOrigin(params[0])
} else {
if (params[1].startsWith('url(')) {
return loadFontOrigin(params[1].slice(5, -2))
}
return loadFontOrigin(params[1])
}
})
}
// 兼容 getDeviceRotation
if (!window.qg.getDeviceRotation) {
defineReadOnlyPropertyContain(window.qg, 'getDeviceRotation', () => {
const data = window.qg.getSystemInfoSync()
if (data.deviceOrientation === 'portrait') {
return {
rotation: 0
}
} else {
return {
rotation: 1
}
}
})
}
// 兼容 subscribeAccelerometer unsubscribeAccelerometer
if (window.qg.startAccelerometer && !window.qg.subscribeAccelerometer) {
defineReadOnlyPropertyContain(window.qg, 'subscribeAccelerometer', window.qg.startAccelerometer)
}
if (window.qg.stopAccelerometer && !window.qg.unsubscribeAccelerometer) {
defineReadOnlyPropertyContain(window.qg, 'unsubscribeAccelerometer', window.qg.stopAccelerometer)
}
// #region 网络适配
// @note: 暂时只针对 OPPO,其他渠道按需使用
if (!isVivo()) {
const xhrOpenProxy = window.XMLHttpRequest.prototype.open
window.XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
this.url = url
return xhrOpenProxy.apply(this, arguments)
}
}
// #endregion
// ### 其他接口适配 ###
// touch
var _jsb = window.jsb
if (!_jsb) {
_jsb = {}
}
var _touches = []
var _getTouchIndex = function _getTouchIndex (touch) {
var element
for (var index = 0; index < _touches.length; index++) {
element = _touches[index]
if (touch.identifier === element.identifier) {
return index
}
}
return -1
}
var _copyObject = function _copyObject (fromObj, toObject) {
for (var key in fromObj) {
if (fromObj.hasOwnProperty (key)) {
toObject[key] = fromObj[key]
}
}
}
var _listenerMap = {}
_listenerMap.touchstart = []
_listenerMap.touchmove = []
_listenerMap.touchend = []
_listenerMap.touchcancel = []
function _addListener (key, value) {
var listenerArr = _listenerMap[key]
for (var index = 0, length = listenerArr.length; index < length; index++) {
if (value === listenerArr[index]) {
return
}
}
listenerArr.push(value)
}
function _removeListener (key, value) {
var listenerArr = _listenerMap[key] || []
var length = listenerArr.length
for (var index = 0; index < length; ++index) {
if (value === listenerArr[index]) {
listenerArr.splice(index, 1)
return
}
}
}
var _hasDellWith = false
var _systemInfo = window.qg.getSystemInfoSync()
if (window.innerWidth && _systemInfo.windowWidth !== window.innerWidth) {
_hasDellWith = true
}
var _touchEventHandlerFactory = function _touchEventHandlerFactory (type) {
return function (changedTouches) {
if (typeof changedTouches === 'function') {
_addListener(type, changedTouches)
return
}
var touchEvent = new window.TouchEvent(type)
var index
if (type === 'touchstart') {
changedTouches.forEach(function (touch) {
index = _getTouchIndex(touch)
if (index >= 0) {
_copyObject(touch, _touches[index])
} else {
var tmp = {}
_copyObject(touch, tmp)
_touches.push(tmp)
}
})
} else if (type === 'touchmove') {
changedTouches.forEach(function (element) {
index = _getTouchIndex(element)
if (index >= 0) {
_copyObject(element, _touches[index])
}
})
} else if (type === 'touchend' || type === 'touchcancel') {
changedTouches.forEach(function (element) {
index = _getTouchIndex(element)
if (index >= 0) {
_touches.splice(index, 1)
}
})
}
var touches = [].concat(_touches)
var _changedTouches = []
changedTouches.forEach(function (touch) {
var length = touches.length
for (var _index = 0; _index < length; ++_index) {
var _touch = touches[_index]
if (touch.identifier === _touch.identifier) {
_changedTouches.push(_touch)
return
}
}
_changedTouches.push(touch)
})
touchEvent.touches = touches
touchEvent.targetTouches = touches
touchEvent.changedTouches = _changedTouches
if (_hasDellWith) {
touches.forEach(function (touch) {
touch.clientX /= window.devicePixelRatio
touch.clientY /= window.devicePixelRatio
touch.pageX /= window.devicePixelRatio
touch.pageY /= window.devicePixelRatio
})
if (type === 'touchcancel' || type === 'touchend') {
_changedTouches.forEach(function (touch) {
touch.clientX /= window.devicePixelRatio
touch.clientY /= window.devicePixelRatio
touch.pageX /= window.devicePixelRatio
touch.pageY /= window.devicePixelRatio
})
}
}
var listenerArr = _listenerMap[type]
var length = listenerArr.length
for (var _index2 = 0; _index2 < length; _index2++) {
listenerArr[_index2](touchEvent)
}
}
}
if (!window.qg.onTouchStart) {
_jsb.onTouchStart = _touchEventHandlerFactory('touchstart')
_jsb.offTouchStart = function (callback) {
_removeListener('touchstart', callback)
}
window.qg.onTouchStart = _jsb.onTouchStart.bind(_jsb)
window.qg.offTouchStart = _jsb.offTouchStart.bind(_jsb)
}
if (!window.qg.onTouchMove) {
_jsb.onTouchMove = _touchEventHandlerFactory('touchmove')
_jsb.offTouchMove = function (callback) {
_removeListener('touchmove', callback)
}
window.qg.onTouchMove = _jsb.onTouchMove.bind(_jsb)
window.qg.offTouchMove = _jsb.offTouchMove.bind(_jsb)
}
if (!window.qg.onTouchCancel) {
_jsb.onTouchCancel = _touchEventHandlerFactory('touchcancel')
_jsb.offTouchCancel = function (callback) {
_removeListener('touchcancel', callback)
}
window.qg.onTouchCancel = _jsb.onTouchCancel.bind(_jsb)
window.qg.offTouchCancel = _jsb.offTouchCancel.bind(_jsb)
}
if (!window.qg.onTouchEnd) {
_jsb.onTouchEnd = _touchEventHandlerFactory('touchend')
_jsb.offTouchEnd = function (callback) {
_removeListener('touchend', callback)
}
window.qg.onTouchEnd = _jsb.onTouchEnd.bind(_jsb)
window.qg.offTouchEnd = _jsb.offTouchEnd.bind(_jsb)
}
// 小米适配=====================
if (window.TouchEvent) {
defineReadOnlyPropertyContain(window, 'TouchEvent', window.TouchEvent)
}
if (window.Event) {
defineReadOnlyPropertyContain(window, 'Event', window.Event)
}
if (window.mainCanvas) {
defineReadOnlyPropertyContain(window, 'mainCanvas', window.mainCanvas)
}
// document 获取canvas适配
if(isMi()){
const _preDocument = window.document
const _oldCreateElement = _preDocument.createElement
const _oldGetElementById = _preDocument.getElementById
let _document = _preDocument
Object.defineProperty(window, 'document', {
configurable: true,
enumerable: true,
get: () => {
return _document
},
set: (value) => {
if(value){
const _createElement = value.createElement
const _getElementById = value.getElementById
value.createElement = function(tagName){
if(tagName.toUpperCase() === 'CANVAS') return _oldCreateElement.call(_preDocument, 'canvas')
return _createElement.call(this, tagName)
}
value.getElementById = function(id){
if(id.toUpperCase() === 'CANVAS') return _oldGetElementById.call(_preDocument, 'canvas')
return _getElementById.call(this, id)
}
}
_document = value
}
})
}
// cocos 引擎 Image 适配
if(isMi() && window.__miEngineType === 'cocos'){
const preRequire = window.require
const getAbsolutePath = (path) => {
if (!path || typeof path !== 'string') {
console.error('require path is not a string', path);
return null
}
if (path.startsWith('/') || path.startsWith('.')) {
return path;
}
return '/' + path;
}
window.require = function(path){
return preRequire(getAbsolutePath(path));
}
}
if(isMi() && window.__miEngineType === 'cocos' && !window.__disableTransformPath){
const data = [
["internal://tmp/","rt-temp:/"],
["internal://cache/","rt-user:/"],
["internal://files/","rt-user://internal-files/"]
]
const regCache = {};
const transformPath = (data, path) => {
for (let i = 0; i < data.length; i++) {
regCache[data[i][0]] = regCache[data[i][0]] || new RegExp(data[i][0], 'i');
if (regCache[data[i][0]].test(path)) {
return path.replace(regCache[data[i][0]], data[i][1])
}
}
return null
};
const formatPath = (path)=>{
return transformPath(data, path) || path
}
const _Image= window.Image
Object.defineProperty(window.qg, 'createImage', {
enumerable: true,
configurable: true,
value: () => {
const img = new _Image()
// 在原型链上查找src属性描述符
let proto = Object.getPrototypeOf(img);
let descriptor;
while (proto && !descriptor) {
descriptor = Object.getOwnPropertyDescriptor(proto, 'src');
proto = Object.getPrototypeOf(proto);
}
if (!descriptor) {
console.error('Cannot find src property descriptor');
descriptor = img;
}
const originalSetter = descriptor.set;
const originalGetter = descriptor.get;
Object.defineProperty(img, 'src', {
configurable: true,
enumerable: true,
get: function() {
return this._src || originalGetter.call(this);
},
set: function(value) {
this._src = value;
const transformedPath = formatPath(value);
return originalSetter.call(this, transformedPath);
}
});
return img;
}
});
Object.defineProperty(window, 'Image', {
set(){},
get(){
return function(width, height) {
return qg.createImage(width, height);
}
}
});
window.loadRuntime = ()=>{
return qg;
}
// ------------------
class EventEmitter {
constructor() {
this.events = {}
}
on(eventName, callback) {
if (!this.events[eventName]) {
this.events[eventName] = []
}
this.events[eventName].push(callback)
}
off(eventName, callback) {
if (!this.events[eventName]) return
const index = this.events[eventName].indexOf(callback)
if (index > -1) {
this.events[eventName].splice(index, 1)
}
}
emit(eventName, data) {
if (!this.events[eventName]) return
this.events[eventName].forEach(callback => {
try {
callback(data)
} catch (error) {
console.error(`Error in event callback for ${eventName}:`, error)
}
})
}
}
class RequestTask {
constructor(inst, eventEmitter) {
this._inst = inst
this._eventEmitter = eventEmitter
}
abort() {
this._inst.abort()
}
onHeadersReceived(callback) {
this._eventEmitter.on('HeadersReceived', callback)
}
offHeadersReceived(callback) {
this._eventEmitter.off('HeadersReceived', callback)
}
onChunkReceived(callback) {
this._eventEmitter.on('ChunkReceived', callback)
}
offChunkReceived(callback) {
this._eventEmitter.off('ChunkReceived', callback)
}
}
function generateQueryString(data) {
let queryString = ''
data = data || []
Object.keys(data).forEach(key => {
queryString += `&${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`
})
queryString = queryString.substr(1)
return queryString
}
function formCRLFStrToObj(originStr) {
const destObj = {}
const arr = originStr.split(/[\r\n](?=\w)/)
arr.forEach(item => {
const arr = item.split(':')
destObj[arr[0].trim()] = arr[1].trim()
})
return destObj
}
function execMethod(option, methods, args = []) {
methods.forEach(method => {
if (typeof option[method] === 'function') {
try {
option[method](...args)
} catch (error) {
console.error(`Error executing ${method} callback:`, error)
}
}
})
}
const _XMLHttpRequest = XMLHttpRequest
qg.request = function(option = {}) {
const xhr = new _XMLHttpRequest()
const eventEmitter = new EventEmitter()
const requestTask = new RequestTask(xhr, eventEmitter)
const reqHeader = option.header || {}
let resHeader
let hasContentType = false
let headerContentTypeVal
let reqData = option.data
let prevDataLength = 0
let isCompleted = false
option.method = option.method || 'GET'
option.dataType = option.dataType || 'json'
option.responseType = option.responseType || 'text'
Object.keys(reqHeader).some(key => {
if (!hasContentType && key.toLowerCase() === 'content-type') {
hasContentType = true
headerContentTypeVal = reqHeader[key]
return true
}
return false
})
if (!hasContentType) {
reqHeader['content-type'] = 'application/json'
}
if (typeof option.data === 'objec