mirror of
https://github.com/smallmain/cocos-enhance-kit.git
synced 2025-10-09 03:45:23 +00:00
[adapters] 增加小游戏适配部分源码
This commit is contained in:
288
adapters/common/cache-manager.js
Normal file
288
adapters/common/cache-manager.js
Normal file
@@ -0,0 +1,288 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2019 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
https://www.cocos.com/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of cache-manager software and associated engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in cache-manager License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
const { getUserDataPath, readJsonSync, makeDirSync, writeFileSync, copyFile, downloadFile, writeFile, deleteFile, rmdirSync, unzip, isOutOfStorage } = window.fsUtils;
|
||||
|
||||
var checkNextPeriod = false;
|
||||
var writeCacheFileList = null;
|
||||
var startWrite = false;
|
||||
var nextCallbacks = [];
|
||||
var callbacks = [];
|
||||
var cleaning = false;
|
||||
var suffix = 0;
|
||||
const REGEX = /^https?:\/\/.*/;
|
||||
|
||||
var cacheManager = {
|
||||
|
||||
cacheDir: 'gamecaches',
|
||||
|
||||
cachedFileName: 'cacheList.json',
|
||||
|
||||
// whether or not cache asset into user's storage space
|
||||
cacheEnabled: true,
|
||||
|
||||
// whether or not auto clear cache when storage ran out
|
||||
autoClear: true,
|
||||
|
||||
// cache one per cycle
|
||||
cacheInterval: 500,
|
||||
|
||||
deleteInterval: 500,
|
||||
|
||||
writeFileInterval: 2000,
|
||||
|
||||
// whether or not storage space has run out
|
||||
outOfStorage: false,
|
||||
|
||||
tempFiles: null,
|
||||
|
||||
cachedFiles: null,
|
||||
|
||||
cacheQueue: {},
|
||||
|
||||
version: '1.0',
|
||||
|
||||
getCache (url) {
|
||||
return this.cachedFiles.has(url) ? this.cachedFiles.get(url).url : '';
|
||||
},
|
||||
|
||||
getTemp (url) {
|
||||
return this.tempFiles.has(url) ? this.tempFiles.get(url) : '';
|
||||
},
|
||||
|
||||
init () {
|
||||
this.cacheDir = getUserDataPath() + '/' + this.cacheDir;
|
||||
var cacheFilePath = this.cacheDir + '/' + this.cachedFileName;
|
||||
var result = readJsonSync(cacheFilePath);
|
||||
if (result instanceof Error || !result.version) {
|
||||
if (!(result instanceof Error)) rmdirSync(this.cacheDir, true);
|
||||
this.cachedFiles = new cc.AssetManager.Cache();
|
||||
makeDirSync(this.cacheDir, true);
|
||||
writeFileSync(cacheFilePath, JSON.stringify({ files: this.cachedFiles._map, version: this.version }), 'utf8');
|
||||
}
|
||||
else {
|
||||
this.cachedFiles = new cc.AssetManager.Cache(result.files);
|
||||
}
|
||||
this.tempFiles = new cc.AssetManager.Cache();
|
||||
},
|
||||
|
||||
updateLastTime (url) {
|
||||
if (this.cachedFiles.has(url)) {
|
||||
var cache = this.cachedFiles.get(url);
|
||||
cache.lastTime = Date.now();
|
||||
}
|
||||
},
|
||||
|
||||
_write () {
|
||||
writeCacheFileList = null;
|
||||
startWrite = true;
|
||||
writeFile(this.cacheDir + '/' + this.cachedFileName, JSON.stringify({ files: this.cachedFiles._map, version: this.version }), 'utf8', function () {
|
||||
startWrite = false;
|
||||
for (let i = 0, j = callbacks.length; i < j; i++) {
|
||||
callbacks[i]();
|
||||
}
|
||||
callbacks.length = 0;
|
||||
callbacks.push.apply(callbacks, nextCallbacks);
|
||||
nextCallbacks.length = 0;
|
||||
});
|
||||
},
|
||||
|
||||
writeCacheFile (cb) {
|
||||
if (!writeCacheFileList) {
|
||||
writeCacheFileList = setTimeout(this._write.bind(this), this.writeFileInterval);
|
||||
if (startWrite === true) {
|
||||
cb && nextCallbacks.push(cb);
|
||||
}
|
||||
else {
|
||||
cb && callbacks.push(cb);
|
||||
}
|
||||
} else {
|
||||
cb && callbacks.push(cb);
|
||||
}
|
||||
},
|
||||
|
||||
_cache () {
|
||||
var self = this;
|
||||
for (var id in this.cacheQueue) {
|
||||
var { srcUrl, isCopy, cacheBundleRoot } = this.cacheQueue[id];
|
||||
var time = Date.now().toString();
|
||||
|
||||
var localPath = '';
|
||||
|
||||
if (cacheBundleRoot) {
|
||||
localPath = `${this.cacheDir}/${cacheBundleRoot}/${time}${suffix++}${cc.path.extname(id)}`;
|
||||
}
|
||||
else {
|
||||
localPath = `${this.cacheDir}/${time}${suffix++}${cc.path.extname(id)}`;
|
||||
}
|
||||
|
||||
function callback (err) {
|
||||
checkNextPeriod = false;
|
||||
if (err) {
|
||||
if (isOutOfStorage(err.message)) {
|
||||
self.outOfStorage = true;
|
||||
self.autoClear && self.clearLRU();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
self.cachedFiles.add(id, { bundle: cacheBundleRoot, url: localPath, lastTime: time });
|
||||
delete self.cacheQueue[id];
|
||||
self.writeCacheFile();
|
||||
}
|
||||
if (!cc.js.isEmptyObject(self.cacheQueue)) {
|
||||
checkNextPeriod = true;
|
||||
setTimeout(self._cache.bind(self), self.cacheInterval);
|
||||
}
|
||||
}
|
||||
if (!isCopy) {
|
||||
downloadFile(srcUrl, localPath, null, callback);
|
||||
}
|
||||
else {
|
||||
copyFile(srcUrl, localPath, callback);
|
||||
}
|
||||
return;
|
||||
}
|
||||
checkNextPeriod = false;
|
||||
},
|
||||
|
||||
cacheFile (id, srcUrl, cacheEnabled, cacheBundleRoot, isCopy) {
|
||||
cacheEnabled = cacheEnabled !== undefined ? cacheEnabled : this.cacheEnabled;
|
||||
if (!cacheEnabled || this.cacheQueue[id] || this.cachedFiles.has(id)) return;
|
||||
|
||||
this.cacheQueue[id] = { srcUrl, cacheBundleRoot, isCopy };
|
||||
if (!checkNextPeriod) {
|
||||
checkNextPeriod = true;
|
||||
if (!this.outOfStorage) {
|
||||
setTimeout(this._cache.bind(this), this.cacheInterval);
|
||||
}
|
||||
else {
|
||||
checkNextPeriod = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
clearCache () {
|
||||
rmdirSync(this.cacheDir, true);
|
||||
this.cachedFiles = new cc.AssetManager.Cache();
|
||||
makeDirSync(this.cacheDir, true);
|
||||
var cacheFilePath = this.cacheDir + '/' + this.cachedFileName;
|
||||
this.outOfStorage = false;
|
||||
writeFileSync(cacheFilePath, JSON.stringify({ files: this.cachedFiles._map, version: this.version }), 'utf8');
|
||||
cc.assetManager.bundles.forEach(bundle => {
|
||||
if (REGEX.test(bundle.base)) this.makeBundleFolder(bundle.name);
|
||||
});
|
||||
},
|
||||
|
||||
clearLRU () {
|
||||
if (cleaning) return;
|
||||
cleaning = true;
|
||||
var caches = [];
|
||||
var self = this;
|
||||
this.cachedFiles.forEach(function (val, key) {
|
||||
if (val.bundle === 'internal') return;
|
||||
if (self._isZipFile(key) && cc.assetManager.bundles.find(bundle => bundle.base.indexOf(val.url) !== -1)) return;
|
||||
caches.push({ originUrl: key, url: val.url, lastTime: val.lastTime });
|
||||
});
|
||||
caches.sort(function (a, b) {
|
||||
return a.lastTime - b.lastTime;
|
||||
});
|
||||
caches.length = Math.floor(caches.length / 3);
|
||||
if (caches.length === 0) return;
|
||||
for (var i = 0, l = caches.length; i < l; i++) {
|
||||
this.cachedFiles.remove(caches[i].originUrl);
|
||||
}
|
||||
|
||||
this.writeCacheFile(function () {
|
||||
function deferredDelete () {
|
||||
var item = caches.pop();
|
||||
if (self._isZipFile(item.originUrl)) {
|
||||
rmdirSync(item.url, true);
|
||||
self._deleteFileCB();
|
||||
}
|
||||
else {
|
||||
deleteFile(item.url, self._deleteFileCB.bind(self));
|
||||
}
|
||||
if (caches.length > 0) {
|
||||
setTimeout(deferredDelete, self.deleteInterval);
|
||||
}
|
||||
else {
|
||||
cleaning = false;
|
||||
}
|
||||
}
|
||||
setTimeout(deferredDelete, self.deleteInterval);
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
removeCache (url) {
|
||||
if (this.cachedFiles.has(url)) {
|
||||
var self = this;
|
||||
var path = this.cachedFiles.remove(url).url;
|
||||
this.writeCacheFile(function () {
|
||||
if (self._isZipFile(url)) {
|
||||
rmdirSync(path, true);
|
||||
self._deleteFileCB();
|
||||
}
|
||||
else {
|
||||
deleteFile(path, self._deleteFileCB.bind(self));
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_deleteFileCB (err) {
|
||||
if (!err) this.outOfStorage = false;
|
||||
},
|
||||
|
||||
makeBundleFolder (bundleName) {
|
||||
makeDirSync(this.cacheDir + '/' + bundleName, true);
|
||||
},
|
||||
|
||||
unzipAndCacheBundle (id, zipFilePath, cacheBundleRoot, onComplete) {
|
||||
let time = Date.now().toString();
|
||||
let targetPath = `${this.cacheDir}/${cacheBundleRoot}/${time}${suffix++}`;
|
||||
let self = this;
|
||||
makeDirSync(targetPath, true);
|
||||
unzip(zipFilePath, targetPath, function (err) {
|
||||
if (err) {
|
||||
rmdirSync(targetPath, true);
|
||||
if (isOutOfStorage(err.message)) {
|
||||
self.outOfStorage = true;
|
||||
self.autoClear && self.clearLRU();
|
||||
}
|
||||
onComplete && onComplete(err);
|
||||
return;
|
||||
}
|
||||
self.cachedFiles.add(id, { bundle: cacheBundleRoot, url: targetPath, lastTime: time });
|
||||
self.writeCacheFile();
|
||||
onComplete && onComplete(null, targetPath);
|
||||
});
|
||||
},
|
||||
|
||||
_isZipFile (url) {
|
||||
return url.slice(-4) === '.zip';
|
||||
},
|
||||
};
|
||||
|
||||
cc.assetManager.cacheManager = module.exports = cacheManager;
|
471
adapters/common/engine/AssetManager.js
Normal file
471
adapters/common/engine/AssetManager.js
Normal file
@@ -0,0 +1,471 @@
|
||||
const cacheManager = require('../cache-manager');
|
||||
const { fs, downloadFile, readText, readArrayBuffer, readJson, loadSubpackage, getUserDataPath, exists } = window.fsUtils;
|
||||
|
||||
const REGEX = /^https?:\/\/.*/;
|
||||
const cachedSubpackageList = {};
|
||||
const downloader = cc.assetManager.downloader;
|
||||
const parser = cc.assetManager.parser;
|
||||
const presets = cc.assetManager.presets;
|
||||
const isSubDomain = __globalAdapter.isSubContext;
|
||||
downloader.maxConcurrency = 8;
|
||||
downloader.maxRequestsPerFrame = 64;
|
||||
presets['scene'].maxConcurrency = 10;
|
||||
presets['scene'].maxRequestsPerFrame = 64;
|
||||
|
||||
let SUBCONTEXT_ROOT, REMOTE_SERVER_ROOT;
|
||||
let subpackages = {}, remoteBundles = {};
|
||||
|
||||
function downloadScript (url, options, onComplete) {
|
||||
if (typeof options === 'function') {
|
||||
onComplete = options;
|
||||
options = null;
|
||||
}
|
||||
if (REGEX.test(url)) {
|
||||
onComplete && onComplete(new Error('Can not load remote scripts'));
|
||||
}
|
||||
else {
|
||||
__cocos_require__(url);
|
||||
onComplete && onComplete(null);
|
||||
}
|
||||
}
|
||||
|
||||
function handleZip (url, options, onComplete) {
|
||||
let cachedUnzip = cacheManager.cachedFiles.get(url);
|
||||
if (cachedUnzip) {
|
||||
cacheManager.updateLastTime(url);
|
||||
onComplete && onComplete(null, cachedUnzip.url);
|
||||
}
|
||||
else if (REGEX.test(url)) {
|
||||
downloadFile(url, null, options.header, options.onFileProgress, function (err, downloadedZipPath) {
|
||||
if (err) {
|
||||
onComplete && onComplete(err);
|
||||
return;
|
||||
}
|
||||
cacheManager.unzipAndCacheBundle(url, downloadedZipPath, options.__cacheBundleRoot__, onComplete);
|
||||
});
|
||||
}
|
||||
else {
|
||||
cacheManager.unzipAndCacheBundle(url, url, options.__cacheBundleRoot__, onComplete);
|
||||
}
|
||||
}
|
||||
|
||||
function downloadDomAudio (url, options, onComplete) {
|
||||
if (typeof options === 'function') {
|
||||
onComplete = options;
|
||||
options = null;
|
||||
}
|
||||
|
||||
let dom;
|
||||
let sys = cc.sys;
|
||||
if (sys.platform === sys.TAOBAO || sys.platform === sys.TAOBAO_MINIGAME) {
|
||||
dom = window.document.createElement('audio');
|
||||
} else {
|
||||
dom = document.createElement('audio');
|
||||
}
|
||||
dom.src = url;
|
||||
|
||||
// HACK: wechat does not callback when load large number of assets
|
||||
onComplete && onComplete(null, dom);
|
||||
}
|
||||
|
||||
function download (url, func, options, onFileProgress, onComplete) {
|
||||
var result = transformUrl(url, options);
|
||||
if (result.inLocal) {
|
||||
func(result.url, options, onComplete);
|
||||
}
|
||||
else if (result.inCache) {
|
||||
cacheManager.updateLastTime(url);
|
||||
func(result.url, options, function (err, data) {
|
||||
if (err) {
|
||||
cacheManager.removeCache(url);
|
||||
}
|
||||
onComplete(err, data);
|
||||
});
|
||||
}
|
||||
else {
|
||||
downloadFile(url, null, options.header, onFileProgress, function (err, path) {
|
||||
if (err) {
|
||||
onComplete(err, null);
|
||||
return;
|
||||
}
|
||||
func(path, options, function (err, data) {
|
||||
if (!err) {
|
||||
cacheManager.tempFiles.add(url, path);
|
||||
cacheManager.cacheFile(url, path, options.cacheEnabled, options.__cacheBundleRoot__, true);
|
||||
}
|
||||
onComplete(err, data);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function parseArrayBuffer (url, options, onComplete) {
|
||||
readArrayBuffer(url, onComplete);
|
||||
}
|
||||
|
||||
function parseText (url, options, onComplete) {
|
||||
readText(url, onComplete);
|
||||
}
|
||||
|
||||
function parseJson (url, options, onComplete) {
|
||||
readJson(url, onComplete);
|
||||
}
|
||||
|
||||
function downloadText (url, options, onComplete) {
|
||||
download(url, parseText, options, options.onFileProgress, onComplete);
|
||||
}
|
||||
|
||||
var downloadJson = !isSubDomain ? function (url, options, onComplete) {
|
||||
download(url, parseJson, options, options.onFileProgress, onComplete);
|
||||
} : function (url, options, onComplete) {
|
||||
var { url } = transformUrl(url, options);
|
||||
url = url.slice(SUBCONTEXT_ROOT.length + 1); // remove subcontext root in url
|
||||
var content = __cocos_require__(cc.path.changeExtname(url, '.js'));
|
||||
onComplete && onComplete(null, content);
|
||||
}
|
||||
|
||||
var loadFont = !isSubDomain ? function (url, options, onComplete) {
|
||||
var fontFamily = __globalAdapter.loadFont(url);
|
||||
onComplete(null, fontFamily || 'Arial');
|
||||
} : function (url, options, onComplete) {
|
||||
onComplete(null, 'Arial');
|
||||
}
|
||||
|
||||
function doNothing (content, options, onComplete) {
|
||||
exists(content, (existence) => {
|
||||
if (existence) {
|
||||
onComplete(null, content);
|
||||
} else {
|
||||
onComplete(new Error(`file ${content} does not exist!`));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function downloadAsset (url, options, onComplete) {
|
||||
download(url, doNothing, options, options.onFileProgress, onComplete);
|
||||
}
|
||||
|
||||
function subdomainTransformUrl (url, options, onComplete) {
|
||||
var { url } = transformUrl(url, options);
|
||||
onComplete(null, url);
|
||||
}
|
||||
|
||||
function downloadBundle (nameOrUrl, options, onComplete) {
|
||||
let bundleName = cc.path.basename(nameOrUrl);
|
||||
var version = options.version || cc.assetManager.downloader.bundleVers[bundleName];
|
||||
const suffix = version ? `${version}.` : '';
|
||||
|
||||
function getConfigPathForSubPackage () {
|
||||
let sys = cc.sys;
|
||||
if (sys.platform === sys.TAOBAO_MINIGAME) {
|
||||
return `${bundleName}/config.${suffix}json`;
|
||||
}
|
||||
return `subpackages/${bundleName}/config.${suffix}json`;
|
||||
}
|
||||
|
||||
function appendBaseToJsonData (data) {
|
||||
if (!data) return;
|
||||
|
||||
let sys = cc.sys;
|
||||
if (sys.platform === sys.TAOBAO_MINIGAME) {
|
||||
data.base = `${bundleName}/`;
|
||||
} else {
|
||||
data.base = `subpackages/${bundleName}/`;
|
||||
}
|
||||
}
|
||||
|
||||
if (subpackages[bundleName]) {
|
||||
var config = getConfigPathForSubPackage();
|
||||
let loadedCb = function () {
|
||||
downloadJson(config, options, function (err, data) {
|
||||
appendBaseToJsonData(data);
|
||||
onComplete(err, data);
|
||||
});
|
||||
};
|
||||
if (cachedSubpackageList[bundleName]) {
|
||||
return loadedCb();
|
||||
}
|
||||
loadSubpackage(bundleName, options.onFileProgress, function (err) {
|
||||
if (err) {
|
||||
onComplete(err, null);
|
||||
return;
|
||||
}
|
||||
cachedSubpackageList[bundleName] = true;
|
||||
loadedCb();
|
||||
});
|
||||
}
|
||||
else {
|
||||
let js, url;
|
||||
if (REGEX.test(nameOrUrl) || (!isSubDomain && nameOrUrl.startsWith(getUserDataPath()))) {
|
||||
url = nameOrUrl;
|
||||
js = `src/scripts/${bundleName}/index.js`;
|
||||
cacheManager.makeBundleFolder(bundleName);
|
||||
}
|
||||
else {
|
||||
if (remoteBundles[bundleName]) {
|
||||
url = `${REMOTE_SERVER_ROOT}remote/${bundleName}`;
|
||||
js = `src/scripts/${bundleName}/index.js`;
|
||||
cacheManager.makeBundleFolder(bundleName);
|
||||
}
|
||||
else {
|
||||
url = `assets/${bundleName}`;
|
||||
js = `assets/${bundleName}/index.js`;
|
||||
}
|
||||
}
|
||||
__cocos_require__(js);
|
||||
options.__cacheBundleRoot__ = bundleName;
|
||||
var config = `${url}/config.${version ? version + '.' : ''}json`;
|
||||
downloadJson(config, options, function (err, data) {
|
||||
if (err) {
|
||||
onComplete && onComplete(err);
|
||||
return;
|
||||
}
|
||||
if (data.isZip) {
|
||||
let zipVersion = data.zipVersion;
|
||||
let zipUrl = `${url}/res.${zipVersion ? zipVersion + '.' : ''}zip`;
|
||||
handleZip(zipUrl, options, function (err, unzipPath) {
|
||||
if (err) {
|
||||
onComplete && onComplete(err);
|
||||
return;
|
||||
}
|
||||
data.base = unzipPath + '/res/';
|
||||
// PATCH: for android alipay version before v10.1.95 (v10.1.95 included)
|
||||
// to remove in the future
|
||||
let sys = cc.sys;
|
||||
if (sys.platform === sys.ALIPAY_GAME && sys.os === sys.OS_ANDROID) {
|
||||
let resPath = unzipPath + 'res/';
|
||||
if (fs.accessSync({path: resPath}).success) {
|
||||
data.base = resPath;
|
||||
}
|
||||
}
|
||||
onComplete && onComplete(null, data);
|
||||
});
|
||||
}
|
||||
else {
|
||||
data.base = url + '/';
|
||||
onComplete && onComplete(null, data);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const originParsePVRTex = parser.parsePVRTex;
|
||||
let parsePVRTex = function (file, options, onComplete) {
|
||||
readArrayBuffer(file, function (err, data) {
|
||||
if (err) return onComplete(err);
|
||||
originParsePVRTex(data, options, onComplete);
|
||||
});
|
||||
};
|
||||
|
||||
const originParseASTCTex = parser.parseASTCTex;
|
||||
let parseASTCTex = function (file, options, onComplete) {
|
||||
readArrayBuffer(file, function (err, data) {
|
||||
if (err) return onComplete(err);
|
||||
originParseASTCTex(data, options, onComplete);
|
||||
});
|
||||
};
|
||||
|
||||
const originParsePKMTex = parser.parsePKMTex;
|
||||
let parsePKMTex = function (file, options, onComplete) {
|
||||
readArrayBuffer(file, function (err, data) {
|
||||
if (err) return onComplete(err);
|
||||
originParsePKMTex(data, options, onComplete);
|
||||
});
|
||||
};
|
||||
|
||||
function parsePlist (url, options, onComplete) {
|
||||
readText(url, function (err, file) {
|
||||
var result = null;
|
||||
if (!err) {
|
||||
result = cc.plistParser.parse(file);
|
||||
if (!result) err = new Error('parse failed');
|
||||
}
|
||||
onComplete && onComplete(err, result);
|
||||
});
|
||||
}
|
||||
|
||||
let downloadImage = isSubDomain ? subdomainTransformUrl : downloadAsset;
|
||||
downloader.downloadDomAudio = downloadDomAudio;
|
||||
downloader.downloadScript = downloadScript;
|
||||
parser.parsePVRTex = parsePVRTex;
|
||||
parser.parsePKMTex = parsePKMTex;
|
||||
parser.parseASTCTex = parseASTCTex;
|
||||
|
||||
downloader.register({
|
||||
'.js' : downloadScript,
|
||||
|
||||
// Audio
|
||||
'.mp3' : downloadAsset,
|
||||
'.ogg' : downloadAsset,
|
||||
'.wav' : downloadAsset,
|
||||
'.m4a' : downloadAsset,
|
||||
|
||||
// Image
|
||||
'.png' : downloadImage,
|
||||
'.jpg' : downloadImage,
|
||||
'.bmp' : downloadImage,
|
||||
'.jpeg' : downloadImage,
|
||||
'.gif' : downloadImage,
|
||||
'.ico' : downloadImage,
|
||||
'.tiff' : downloadImage,
|
||||
'.image' : downloadImage,
|
||||
'.webp' : downloadImage,
|
||||
'.pvr': downloadAsset,
|
||||
'.pkm': downloadAsset,
|
||||
'.astc': downloadAsset,
|
||||
|
||||
'.font': downloadAsset,
|
||||
'.eot': downloadAsset,
|
||||
'.ttf': downloadAsset,
|
||||
'.woff': downloadAsset,
|
||||
'.svg': downloadAsset,
|
||||
'.ttc': downloadAsset,
|
||||
|
||||
// Txt
|
||||
'.txt' : downloadAsset,
|
||||
'.xml' : downloadAsset,
|
||||
'.vsh' : downloadAsset,
|
||||
'.fsh' : downloadAsset,
|
||||
'.atlas' : downloadAsset,
|
||||
|
||||
'.tmx' : downloadAsset,
|
||||
'.tsx' : downloadAsset,
|
||||
'.plist' : downloadAsset,
|
||||
'.fnt' : downloadAsset,
|
||||
|
||||
'.json' : downloadJson,
|
||||
'.ExportJson' : downloadAsset,
|
||||
|
||||
'.binary' : downloadAsset,
|
||||
'.bin': downloadAsset,
|
||||
'.dbbin': downloadAsset,
|
||||
'.skel': downloadAsset,
|
||||
|
||||
'.mp4': downloadAsset,
|
||||
'.avi': downloadAsset,
|
||||
'.mov': downloadAsset,
|
||||
'.mpg': downloadAsset,
|
||||
'.mpeg': downloadAsset,
|
||||
'.rm': downloadAsset,
|
||||
'.rmvb': downloadAsset,
|
||||
|
||||
'bundle': downloadBundle,
|
||||
|
||||
'default': downloadText,
|
||||
});
|
||||
|
||||
parser.register({
|
||||
'.png' : downloader.downloadDomImage,
|
||||
'.jpg' : downloader.downloadDomImage,
|
||||
'.bmp' : downloader.downloadDomImage,
|
||||
'.jpeg' : downloader.downloadDomImage,
|
||||
'.gif' : downloader.downloadDomImage,
|
||||
'.ico' : downloader.downloadDomImage,
|
||||
'.tiff' : downloader.downloadDomImage,
|
||||
'.image' : downloader.downloadDomImage,
|
||||
'.webp' : downloader.downloadDomImage,
|
||||
'.pvr': parsePVRTex,
|
||||
'.pkm': parsePKMTex,
|
||||
'.astc': parseASTCTex,
|
||||
|
||||
'.font': loadFont,
|
||||
'.eot': loadFont,
|
||||
'.ttf': loadFont,
|
||||
'.woff': loadFont,
|
||||
'.svg': loadFont,
|
||||
'.ttc': loadFont,
|
||||
|
||||
// Audio
|
||||
'.mp3' : downloadDomAudio,
|
||||
'.ogg' : downloadDomAudio,
|
||||
'.wav' : downloadDomAudio,
|
||||
'.m4a' : downloadDomAudio,
|
||||
|
||||
// Txt
|
||||
'.txt' : parseText,
|
||||
'.xml' : parseText,
|
||||
'.vsh' : parseText,
|
||||
'.fsh' : parseText,
|
||||
'.atlas' : parseText,
|
||||
|
||||
'.tmx' : parseText,
|
||||
'.tsx' : parseText,
|
||||
'.fnt' : parseText,
|
||||
'.plist' : parsePlist,
|
||||
|
||||
'.binary' : parseArrayBuffer,
|
||||
'.bin': parseArrayBuffer,
|
||||
'.dbbin': parseArrayBuffer,
|
||||
'.skel': parseArrayBuffer,
|
||||
|
||||
'.ExportJson' : parseJson,
|
||||
});
|
||||
|
||||
var transformUrl = !isSubDomain ? function (url, options) {
|
||||
var inLocal = false;
|
||||
var inCache = false;
|
||||
var isInUserDataPath = url.startsWith(getUserDataPath());
|
||||
if (isInUserDataPath) {
|
||||
inLocal = true;
|
||||
}
|
||||
else if (REGEX.test(url)) {
|
||||
if (!options.reload) {
|
||||
var cache = cacheManager.cachedFiles.get(url);
|
||||
if (cache) {
|
||||
inCache = true;
|
||||
url = cache.url;
|
||||
}
|
||||
else {
|
||||
var tempUrl = cacheManager.tempFiles.get(url);
|
||||
if (tempUrl) {
|
||||
inLocal = true;
|
||||
url = tempUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
inLocal = true;
|
||||
}
|
||||
return { url, inLocal, inCache };
|
||||
} : function (url, options) {
|
||||
if (!REGEX.test(url)) {
|
||||
url = SUBCONTEXT_ROOT + '/' + url;
|
||||
}
|
||||
return { url };
|
||||
}
|
||||
|
||||
if (!isSubDomain) {
|
||||
cc.assetManager.transformPipeline.append(function (task) {
|
||||
var input = task.output = task.input;
|
||||
for (var i = 0, l = input.length; i < l; i++) {
|
||||
var item = input[i];
|
||||
var options = item.options;
|
||||
if (!item.config) {
|
||||
if (item.ext === 'bundle') continue;
|
||||
options.cacheEnabled = options.cacheEnabled !== undefined ? options.cacheEnabled : false;
|
||||
}
|
||||
else {
|
||||
options.__cacheBundleRoot__ = item.config.name;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var originInit = cc.assetManager.init;
|
||||
cc.assetManager.init = function (options) {
|
||||
originInit.call(cc.assetManager, options);
|
||||
options.subpackages && options.subpackages.forEach(x => subpackages[x] = 'subpackages/' + x);
|
||||
options.remoteBundles && options.remoteBundles.forEach(x => remoteBundles[x] = true);
|
||||
REMOTE_SERVER_ROOT = options.server || '';
|
||||
if (REMOTE_SERVER_ROOT && !REMOTE_SERVER_ROOT.endsWith('/')) REMOTE_SERVER_ROOT += '/';
|
||||
cacheManager.init();
|
||||
};
|
||||
}
|
||||
else {
|
||||
var originInit = cc.assetManager.init;
|
||||
cc.assetManager.init = function (options) {
|
||||
originInit.call(cc.assetManager, options);
|
||||
SUBCONTEXT_ROOT = options.subContextRoot || '';
|
||||
};
|
||||
}
|
||||
|
69
adapters/common/engine/Audio.js
Normal file
69
adapters/common/engine/Audio.js
Normal file
@@ -0,0 +1,69 @@
|
||||
const Audio = cc._Audio;
|
||||
|
||||
if (Audio) {
|
||||
let originGetDuration = Audio.prototype.getDuration;
|
||||
Object.assign(Audio.prototype, {
|
||||
_createElement () {
|
||||
let elem = this._src._nativeAsset;
|
||||
// Reuse dom audio element
|
||||
if (!this._element) {
|
||||
this._element = __globalAdapter.createInnerAudioContext();
|
||||
}
|
||||
this._element.src = elem.src;
|
||||
},
|
||||
|
||||
destroy () {
|
||||
if (this._element) {
|
||||
this._element.destroy();
|
||||
this._element = null;
|
||||
}
|
||||
},
|
||||
|
||||
setCurrentTime (num) {
|
||||
let self = this;
|
||||
this._src && this._src._ensureLoaded(function () {
|
||||
self._element.seek(num);
|
||||
});
|
||||
},
|
||||
|
||||
stop () {
|
||||
let self = this;
|
||||
this._src && this._src._ensureLoaded(function () {
|
||||
// HACK: some platforms won't set currentTime to 0 when stop audio
|
||||
self._element.seek(0);
|
||||
self._element.stop();
|
||||
self._unbindEnded();
|
||||
self.emit('stop');
|
||||
self._state = Audio.State.STOPPED;
|
||||
});
|
||||
},
|
||||
|
||||
_bindEnded () {
|
||||
let elem = this._element;
|
||||
if (elem && elem.onEnded && !this._onended._binded) {
|
||||
this._onended._binded = true;
|
||||
elem.onEnded(this._onended);
|
||||
}
|
||||
},
|
||||
|
||||
_unbindEnded () {
|
||||
let elem = this._element;
|
||||
if (elem && elem.offEnded && this._onended._binded) {
|
||||
this._onended._binded = false;
|
||||
elem.offEnded && elem.offEnded(this._onended);
|
||||
}
|
||||
},
|
||||
|
||||
getDuration () {
|
||||
let duration = originGetDuration.call(this);
|
||||
// HACK: in mini game, if dynamicly load audio, can't get duration from audioClip
|
||||
// because duration is not coming from audio deserialization
|
||||
duration = duration || (this._element ? this._element.duration : 0);
|
||||
return duration;
|
||||
},
|
||||
|
||||
// adapt some special operations on web platform
|
||||
_touchToPlay () { },
|
||||
_forceUpdatingState () { },
|
||||
});
|
||||
}
|
3
adapters/common/engine/AudioEngine.js
Normal file
3
adapters/common/engine/AudioEngine.js
Normal file
@@ -0,0 +1,3 @@
|
||||
if (cc && cc.audioEngine) {
|
||||
cc.audioEngine._maxAudioInstance = 10;
|
||||
}
|
37
adapters/common/engine/DeviceMotionEvent.js
Normal file
37
adapters/common/engine/DeviceMotionEvent.js
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
const inputManager = cc.internal.inputManager;
|
||||
const globalAdapter = window.__globalAdapter;
|
||||
|
||||
Object.assign(inputManager, {
|
||||
setAccelerometerEnabled (isEnable) {
|
||||
let scheduler = cc.director.getScheduler();
|
||||
scheduler.enableForTarget(this);
|
||||
if (isEnable) {
|
||||
this._registerAccelerometerEvent();
|
||||
scheduler.scheduleUpdate(this);
|
||||
}
|
||||
else {
|
||||
this._unregisterAccelerometerEvent();
|
||||
scheduler.unscheduleUpdate(this);
|
||||
}
|
||||
},
|
||||
|
||||
// No need to adapt
|
||||
// setAccelerometerInterval (interval) { },
|
||||
|
||||
_registerAccelerometerEvent () {
|
||||
this._accelCurTime = 0;
|
||||
let self = this;
|
||||
this._acceleration = new cc.Acceleration();
|
||||
globalAdapter.startAccelerometer(function (res) {
|
||||
self._acceleration.x = res.x;
|
||||
self._acceleration.y = res.y;
|
||||
self._acceleration.z = res.y;
|
||||
});
|
||||
},
|
||||
|
||||
_unregisterAccelerometerEvent () {
|
||||
this._accelCurTime = 0;
|
||||
globalAdapter.stopAccelerometer();
|
||||
},
|
||||
});
|
177
adapters/common/engine/Editbox.js
Normal file
177
adapters/common/engine/Editbox.js
Normal file
@@ -0,0 +1,177 @@
|
||||
(function () {
|
||||
if (!(cc && cc.EditBox)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const EditBox = cc.EditBox;
|
||||
const js = cc.js;
|
||||
const KeyboardReturnType = EditBox.KeyboardReturnType;
|
||||
const MAX_VALUE = 65535;
|
||||
const KEYBOARD_HIDE_TIME = 600;
|
||||
let _hideKeyboardTimeout = null;
|
||||
let _currentEditBoxImpl = null;
|
||||
|
||||
function getKeyboardReturnType (type) {
|
||||
switch (type) {
|
||||
case KeyboardReturnType.DEFAULT:
|
||||
case KeyboardReturnType.DONE:
|
||||
return 'done';
|
||||
case KeyboardReturnType.SEND:
|
||||
return 'send';
|
||||
case KeyboardReturnType.SEARCH:
|
||||
return 'search';
|
||||
case KeyboardReturnType.GO:
|
||||
return 'go';
|
||||
case KeyboardReturnType.NEXT:
|
||||
return 'next';
|
||||
}
|
||||
return 'done';
|
||||
}
|
||||
|
||||
const BaseClass = EditBox._ImplClass;
|
||||
function MiniGameEditBoxImpl () {
|
||||
BaseClass.call(this);
|
||||
|
||||
this._eventListeners = {
|
||||
onKeyboardInput: null,
|
||||
onKeyboardConfirm: null,
|
||||
onKeyboardComplete: null,
|
||||
};
|
||||
}
|
||||
|
||||
js.extend(MiniGameEditBoxImpl, BaseClass);
|
||||
EditBox._ImplClass = MiniGameEditBoxImpl;
|
||||
|
||||
Object.assign(MiniGameEditBoxImpl.prototype, {
|
||||
init (delegate) {
|
||||
if (!delegate) {
|
||||
cc.error('EditBox init failed');
|
||||
return;
|
||||
}
|
||||
this._delegate = delegate;
|
||||
},
|
||||
|
||||
beginEditing () {
|
||||
// In case multiply register events
|
||||
if (this._editing) {
|
||||
return;
|
||||
}
|
||||
this._ensureKeyboardHide(() => {
|
||||
let delegate = this._delegate;
|
||||
this._showKeyboard();
|
||||
this._registerKeyboardEvent();
|
||||
this._editing = true;
|
||||
_currentEditBoxImpl = this;
|
||||
delegate.editBoxEditingDidBegan();
|
||||
});
|
||||
},
|
||||
|
||||
endEditing () {
|
||||
this._hideKeyboard();
|
||||
let cbs = this._eventListeners;
|
||||
cbs.onKeyboardComplete && cbs.onKeyboardComplete();
|
||||
},
|
||||
|
||||
_registerKeyboardEvent () {
|
||||
let self = this;
|
||||
let delegate = this._delegate;
|
||||
let cbs = this._eventListeners;
|
||||
|
||||
cbs.onKeyboardInput = function (res) {
|
||||
if (delegate._string !== res.value) {
|
||||
delegate.editBoxTextChanged(res.value);
|
||||
}
|
||||
}
|
||||
|
||||
cbs.onKeyboardConfirm = function (res) {
|
||||
delegate.editBoxEditingReturn();
|
||||
let cbs = self._eventListeners;
|
||||
cbs.onKeyboardComplete && cbs.onKeyboardComplete(res);
|
||||
}
|
||||
|
||||
cbs.onKeyboardComplete = function (res) {
|
||||
self._editing = false;
|
||||
_currentEditBoxImpl = null;
|
||||
self._unregisterKeyboardEvent();
|
||||
if (res && res.value && delegate._string !== res.value) {
|
||||
delegate.editBoxTextChanged(res.value);
|
||||
}
|
||||
delegate.editBoxEditingDidEnded();
|
||||
}
|
||||
|
||||
__globalAdapter.onKeyboardInput(cbs.onKeyboardInput);
|
||||
__globalAdapter.onKeyboardConfirm(cbs.onKeyboardConfirm);
|
||||
__globalAdapter.onKeyboardComplete(cbs.onKeyboardComplete);
|
||||
},
|
||||
|
||||
_unregisterKeyboardEvent () {
|
||||
let cbs = this._eventListeners;
|
||||
|
||||
if (cbs.onKeyboardInput) {
|
||||
__globalAdapter.offKeyboardInput(cbs.onKeyboardInput);
|
||||
cbs.onKeyboardInput = null;
|
||||
}
|
||||
if (cbs.onKeyboardConfirm) {
|
||||
__globalAdapter.offKeyboardConfirm(cbs.onKeyboardConfirm);
|
||||
cbs.onKeyboardConfirm = null;
|
||||
}
|
||||
if (cbs.onKeyboardComplete) {
|
||||
__globalAdapter.offKeyboardComplete(cbs.onKeyboardComplete);
|
||||
cbs.onKeyboardComplete = null;
|
||||
}
|
||||
},
|
||||
|
||||
_otherEditing () {
|
||||
return !!_currentEditBoxImpl && _currentEditBoxImpl !== this && _currentEditBoxImpl._editing;
|
||||
},
|
||||
|
||||
_ensureKeyboardHide (cb) {
|
||||
let otherEditing = this._otherEditing();
|
||||
if (!otherEditing && !_hideKeyboardTimeout) {
|
||||
return cb();
|
||||
}
|
||||
if (_hideKeyboardTimeout) {
|
||||
clearTimeout(_hideKeyboardTimeout);
|
||||
}
|
||||
if (otherEditing) {
|
||||
_currentEditBoxImpl.endEditing();
|
||||
}
|
||||
_hideKeyboardTimeout = setTimeout(() => {
|
||||
_hideKeyboardTimeout = null;
|
||||
cb();
|
||||
}, KEYBOARD_HIDE_TIME);
|
||||
},
|
||||
|
||||
_showKeyboard () {
|
||||
let delegate = this._delegate;
|
||||
let multiline = (delegate.inputMode === EditBox.InputMode.ANY);
|
||||
let maxLength = (delegate.maxLength < 0 ? MAX_VALUE : delegate.maxLength);
|
||||
|
||||
__globalAdapter.showKeyboard({
|
||||
defaultValue: delegate._string,
|
||||
maxLength: maxLength,
|
||||
multiple: multiline,
|
||||
confirmHold: false,
|
||||
confirmType: getKeyboardReturnType(delegate.returnType),
|
||||
success (res) {
|
||||
|
||||
},
|
||||
fail (res) {
|
||||
cc.warn(res.errMsg);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_hideKeyboard () {
|
||||
__globalAdapter.hideKeyboard({
|
||||
success (res) {
|
||||
|
||||
},
|
||||
fail (res) {
|
||||
cc.warn(res.errMsg);
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
})();
|
||||
|
165
adapters/common/engine/Game.js
Normal file
165
adapters/common/engine/Game.js
Normal file
@@ -0,0 +1,165 @@
|
||||
const inputManager = cc.internal.inputManager;
|
||||
const renderer = cc.renderer;
|
||||
const game = cc.game;
|
||||
const dynamicAtlasManager = cc.dynamicAtlasManager;
|
||||
|
||||
let originRun = game.run;
|
||||
Object.assign(game, {
|
||||
_banRunningMainLoop: __globalAdapter.isSubContext,
|
||||
_firstSceneLaunched: false,
|
||||
|
||||
run () {
|
||||
cc.director.once(cc.Director.EVENT_AFTER_SCENE_LAUNCH, () => {
|
||||
this._firstSceneLaunched = true;
|
||||
});
|
||||
originRun.apply(this, arguments);
|
||||
},
|
||||
|
||||
setFrameRate (frameRate) {
|
||||
this.config.frameRate = frameRate;
|
||||
if (__globalAdapter.setPreferredFramesPerSecond) {
|
||||
__globalAdapter.setPreferredFramesPerSecond(frameRate);
|
||||
}
|
||||
else {
|
||||
if (this._intervalId) {
|
||||
window.cancelAnimFrame(this._intervalId);
|
||||
}
|
||||
this._intervalId = 0;
|
||||
this._paused = true;
|
||||
this._setAnimFrame();
|
||||
this._runMainLoop();
|
||||
}
|
||||
},
|
||||
|
||||
_runMainLoop () {
|
||||
if (this._banRunningMainLoop) {
|
||||
return;
|
||||
}
|
||||
var self = this, callback, config = self.config,
|
||||
director = cc.director,
|
||||
skip = true, frameRate = config.frameRate;
|
||||
|
||||
cc.debug.setDisplayStats(config.showFPS);
|
||||
|
||||
callback = function () {
|
||||
if (!self._paused) {
|
||||
self._intervalId = window.requestAnimFrame(callback);
|
||||
if (frameRate === 30 && !__globalAdapter.setPreferredFramesPerSecond) {
|
||||
skip = !skip;
|
||||
if (skip) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
director.mainLoop();
|
||||
}
|
||||
};
|
||||
|
||||
self._intervalId = window.requestAnimFrame(callback);
|
||||
self._paused = false;
|
||||
},
|
||||
|
||||
_initRenderer () {
|
||||
// Avoid setup to be called twice.
|
||||
if (this._rendererInitialized) return;
|
||||
|
||||
// frame and container are useless on minigame platform
|
||||
let sys = cc.sys;
|
||||
if (sys.platform === sys.TAOBAO || sys.platform === sys.TAOBAO_MINIGAME) {
|
||||
this.frame = this.container = window.document.createElement("DIV");
|
||||
} else {
|
||||
this.frame = this.container = document.createElement("DIV");
|
||||
}
|
||||
|
||||
let localCanvas;
|
||||
if (__globalAdapter.isSubContext) {
|
||||
localCanvas = window.sharedCanvas || __globalAdapter.getSharedCanvas();
|
||||
}
|
||||
else if (sys.platform === sys.TAOBAO || sys.platform === sys.TAOBAO_MINIGAME) {
|
||||
localCanvas = window.canvas;
|
||||
}
|
||||
else {
|
||||
localCanvas = canvas;
|
||||
}
|
||||
this.canvas = localCanvas;
|
||||
|
||||
this._determineRenderType();
|
||||
// WebGL context created successfully
|
||||
if (this.renderType === this.RENDER_TYPE_WEBGL) {
|
||||
var opts = {
|
||||
'stencil': true,
|
||||
// MSAA is causing serious performance dropdown on some browsers.
|
||||
'antialias': cc.macro.ENABLE_WEBGL_ANTIALIAS,
|
||||
'alpha': cc.macro.ENABLE_TRANSPARENT_CANVAS,
|
||||
'preserveDrawingBuffer': false,
|
||||
};
|
||||
renderer.initWebGL(localCanvas, opts);
|
||||
this._renderContext = renderer.device._gl;
|
||||
|
||||
// Enable dynamic atlas manager by default
|
||||
if (!cc.macro.CLEANUP_IMAGE_CACHE && dynamicAtlasManager) {
|
||||
dynamicAtlasManager.enabled = true;
|
||||
}
|
||||
}
|
||||
if (!this._renderContext) {
|
||||
this.renderType = this.RENDER_TYPE_CANVAS;
|
||||
// Could be ignored by module settings
|
||||
renderer.initCanvas(localCanvas);
|
||||
this._renderContext = renderer.device._ctx;
|
||||
}
|
||||
|
||||
this._rendererInitialized = true;
|
||||
},
|
||||
|
||||
_initEvents () {
|
||||
let sys = cc.sys;
|
||||
// register system events
|
||||
if (this.config.registerSystemEvent) {
|
||||
inputManager.registerSystemEvent(this.canvas);
|
||||
}
|
||||
|
||||
var hidden = false;
|
||||
|
||||
function onHidden() {
|
||||
if (!hidden) {
|
||||
hidden = true;
|
||||
game.emit(game.EVENT_HIDE);
|
||||
}
|
||||
}
|
||||
|
||||
function onShown(res) {
|
||||
if (hidden) {
|
||||
hidden = false;
|
||||
if (game.renderType === game.RENDER_TYPE_WEBGL) {
|
||||
game._renderContext.finish();
|
||||
}
|
||||
game.emit(game.EVENT_SHOW, res);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: onAudioInterruptionEnd and onAudioInterruptionBegin on ByteDance platform is not designed to behave the same as the ones on WeChat platform,
|
||||
// the callback is invoked on game show or hide on ByteDance platform, while is not invoked on WeChat platform.
|
||||
// See the docs on WeChat: https://developers.weixin.qq.com/minigame/dev/api/base/app/app-event/wx.onAudioInterruptionBegin.html
|
||||
if (sys.platform !== sys.BYTEDANCE_GAME) {
|
||||
__globalAdapter.onAudioInterruptionEnd && __globalAdapter.onAudioInterruptionEnd(function () {
|
||||
if (cc.audioEngine) cc.audioEngine._restore();
|
||||
|
||||
});
|
||||
__globalAdapter.onAudioInterruptionBegin && __globalAdapter.onAudioInterruptionBegin(function () {
|
||||
if (cc.audioEngine) cc.audioEngine._break();
|
||||
});
|
||||
}
|
||||
|
||||
// Maybe not support in open data context
|
||||
__globalAdapter.onShow && __globalAdapter.onShow(onShown);
|
||||
__globalAdapter.onHide && __globalAdapter.onHide(onHidden);
|
||||
|
||||
this.on(game.EVENT_HIDE, function () {
|
||||
game.pause();
|
||||
});
|
||||
this.on(game.EVENT_SHOW, function () {
|
||||
game.resume();
|
||||
});
|
||||
},
|
||||
|
||||
end () { }, // mini game platform not support this api
|
||||
});
|
42
adapters/common/engine/InputManager.js
Normal file
42
adapters/common/engine/InputManager.js
Normal file
@@ -0,0 +1,42 @@
|
||||
const mgr = cc.internal.inputManager;
|
||||
const canvasPosition = {
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight
|
||||
};
|
||||
|
||||
if (mgr) {
|
||||
Object.assign(mgr, {
|
||||
_updateCanvasBoundingRect () {},
|
||||
|
||||
registerSystemEvent (element) {
|
||||
if(this._isRegisterEvent) return;
|
||||
|
||||
this._glView = cc.view;
|
||||
let self = this;
|
||||
|
||||
//register touch event
|
||||
let _touchEventsMap = {
|
||||
onTouchStart: this.handleTouchesBegin,
|
||||
onTouchMove: this.handleTouchesMove,
|
||||
onTouchEnd: this.handleTouchesEnd,
|
||||
onTouchCancel: this.handleTouchesCancel,
|
||||
};
|
||||
|
||||
let registerTouchEvent = function (eventName) {
|
||||
let handler = _touchEventsMap[eventName];
|
||||
__globalAdapter[eventName](function (event) {
|
||||
if (!event.changedTouches) return;
|
||||
handler.call(self, self.getTouchesByEvent(event, canvasPosition));
|
||||
});
|
||||
};
|
||||
|
||||
for (let eventName in _touchEventsMap) {
|
||||
registerTouchEvent(eventName);
|
||||
}
|
||||
|
||||
this._isRegisterEvent = true;
|
||||
},
|
||||
});
|
||||
}
|
5
adapters/common/engine/Screen.js
Normal file
5
adapters/common/engine/Screen.js
Normal file
@@ -0,0 +1,5 @@
|
||||
Object.assign(cc.screen, {
|
||||
autoFullScreen: function (element, onFullScreenChange) {
|
||||
// Not support on mini game
|
||||
}
|
||||
});
|
12
adapters/common/engine/Texture2D.js
Normal file
12
adapters/common/engine/Texture2D.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const Texture2D = cc.Texture2D;
|
||||
|
||||
if (Texture2D) {
|
||||
Object.assign(Texture2D.prototype, {
|
||||
initWithElement (element) {
|
||||
if (!element)
|
||||
return;
|
||||
this._image = element;
|
||||
this.handleLoadedTexture();
|
||||
},
|
||||
});
|
||||
}
|
67
adapters/common/engine/globalAdapter/BaseSystemInfo.js
Normal file
67
adapters/common/engine/globalAdapter/BaseSystemInfo.js
Normal file
@@ -0,0 +1,67 @@
|
||||
function adaptSys (sys, env) {
|
||||
if (!env) {
|
||||
env = __globalAdapter.getSystemInfoSync();
|
||||
}
|
||||
|
||||
var language = env.language || '';
|
||||
var system = env.system || 'iOS';
|
||||
var platform = env.platform || 'iOS';
|
||||
|
||||
sys.isNative = false;
|
||||
sys.isBrowser = false;
|
||||
sys.isMobile = true;
|
||||
sys.language = language.substr(0, 2);
|
||||
sys.languageCode = language.toLowerCase();
|
||||
|
||||
platform = platform.toLowerCase();
|
||||
if (platform === "android") {
|
||||
sys.os = sys.OS_ANDROID;
|
||||
}
|
||||
else if (platform === "ios") {
|
||||
sys.os = sys.OS_IOS;
|
||||
}
|
||||
|
||||
system = system.toLowerCase();
|
||||
// Adaptation to Android P
|
||||
if (system === 'android p') {
|
||||
system = 'android p 9.0';
|
||||
}
|
||||
|
||||
var version = /[\d\.]+/.exec(system);
|
||||
sys.osVersion = version ? version[0] : system;
|
||||
sys.osMainVersion = parseInt(sys.osVersion);
|
||||
sys.browserType = null;
|
||||
sys.browserVersion = null;
|
||||
|
||||
var w = env.windowWidth;
|
||||
var h = env.windowHeight;
|
||||
var ratio = env.pixelRatio || 1;
|
||||
sys.windowPixelResolution = {
|
||||
width: ratio * w,
|
||||
height: ratio * h
|
||||
};
|
||||
|
||||
sys.localStorage = window.localStorage;
|
||||
|
||||
var _supportWebGL = __globalAdapter.isSubContext ? false : true;;
|
||||
var _supportWebp = false;
|
||||
try {
|
||||
var _canvas = document.createElement("canvas");
|
||||
_supportWebp = _canvas.toDataURL('image/webp').startsWith('data:image/webp');
|
||||
}
|
||||
catch (err) { }
|
||||
|
||||
sys.capabilities = {
|
||||
"canvas": true,
|
||||
"opengl": !!_supportWebGL,
|
||||
"webp": _supportWebp
|
||||
};
|
||||
sys.__audioSupport = {
|
||||
ONLY_ONE: false,
|
||||
WEB_AUDIO: false,
|
||||
DELAY_CREATE_CTX: false,
|
||||
format: ['.mp3']
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = adaptSys;
|
25
adapters/common/engine/globalAdapter/ContainerStrategy.js
Normal file
25
adapters/common/engine/globalAdapter/ContainerStrategy.js
Normal file
@@ -0,0 +1,25 @@
|
||||
function adaptContainerStrategy (containerStrategyProto) {
|
||||
containerStrategyProto._setupContainer = function (view, width, height) {
|
||||
// Setup pixel ratio for retina display
|
||||
var devicePixelRatio = view._devicePixelRatio = 1;
|
||||
if (view.isRetinaEnabled()) {
|
||||
devicePixelRatio = view._devicePixelRatio = Math.min(view._maxPixelRatio, window.devicePixelRatio || 1);
|
||||
}
|
||||
// size of sharedCanvas is readonly in subContext
|
||||
if (__globalAdapter.isSubContext) {
|
||||
return;
|
||||
}
|
||||
let locCanvas = cc.game.canvas;
|
||||
// Setup canvas
|
||||
width *= devicePixelRatio;
|
||||
height *= devicePixelRatio;
|
||||
// FIX: black screen on Baidu platform
|
||||
// reset canvas size may call gl.clear(), especially when you call cc.director.loadScene()
|
||||
if (locCanvas.width !== width || locCanvas.height !== height) {
|
||||
locCanvas.width = width;
|
||||
locCanvas.height = height;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = adaptContainerStrategy;
|
43
adapters/common/engine/globalAdapter/View.js
Normal file
43
adapters/common/engine/globalAdapter/View.js
Normal file
@@ -0,0 +1,43 @@
|
||||
function adaptView (viewProto) {
|
||||
Object.assign(viewProto, {
|
||||
_adjustViewportMeta () {
|
||||
// minigame not support
|
||||
},
|
||||
|
||||
setRealPixelResolution (width, height, resolutionPolicy) {
|
||||
// Reset the resolution size and policy
|
||||
this.setDesignResolutionSize(width, height, resolutionPolicy);
|
||||
},
|
||||
|
||||
enableAutoFullScreen (enabled) {
|
||||
cc.warn('cc.view.enableAutoFullScreen() is not supported on minigame platform.');
|
||||
},
|
||||
|
||||
isAutoFullScreenEnabled () {
|
||||
return false;
|
||||
},
|
||||
|
||||
setCanvasSize () {
|
||||
cc.warn('cc.view.setCanvasSize() is not supported on minigame platform.');
|
||||
},
|
||||
|
||||
setFrameSize () {
|
||||
cc.warn('frame size is readonly on minigame platform.');
|
||||
},
|
||||
|
||||
_initFrameSize () {
|
||||
let locFrameSize = this._frameSize;
|
||||
if (__globalAdapter.isSubContext) {
|
||||
let sharedCanvas = window.sharedCanvas || __globalAdapter.getSharedCanvas();
|
||||
locFrameSize.width = sharedCanvas.width;
|
||||
locFrameSize.height = sharedCanvas.height;
|
||||
}
|
||||
else {
|
||||
locFrameSize.width = window.innerWidth;
|
||||
locFrameSize.height = window.innerHeight;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = adaptView;
|
9
adapters/common/engine/globalAdapter/index.js
Normal file
9
adapters/common/engine/globalAdapter/index.js
Normal file
@@ -0,0 +1,9 @@
|
||||
const adapter = window.__globalAdapter;
|
||||
|
||||
Object.assign(adapter, {
|
||||
adaptSys: require('./BaseSystemInfo'),
|
||||
|
||||
adaptView: require('./View'),
|
||||
|
||||
adaptContainerStrategy: require('./ContainerStrategy'),
|
||||
});
|
10
adapters/common/engine/index.js
Normal file
10
adapters/common/engine/index.js
Normal file
@@ -0,0 +1,10 @@
|
||||
require('./Audio');
|
||||
require('./AudioEngine');
|
||||
require('./DeviceMotionEvent');
|
||||
require('./Editbox');
|
||||
require('./Game');
|
||||
require('./InputManager');
|
||||
require('./AssetManager');
|
||||
require('./Screen');
|
||||
require('./Texture2D');
|
||||
require('./misc');
|
1
adapters/common/engine/misc.js
Normal file
1
adapters/common/engine/misc.js
Normal file
@@ -0,0 +1 @@
|
||||
cc.macro.DOWNLOAD_MAX_CONCURRENT = 10;
|
111
adapters/common/utils.js
Normal file
111
adapters/common/utils.js
Normal file
@@ -0,0 +1,111 @@
|
||||
const utils = {
|
||||
/**
|
||||
* @param {Object} target
|
||||
* @param {Object} origin
|
||||
* @param {String} methodName
|
||||
* @param {String} targetMethodName
|
||||
*/
|
||||
cloneMethod (target, origin, methodName, targetMethodName) {
|
||||
if (origin[methodName]) {
|
||||
targetMethodName = targetMethodName || methodName;
|
||||
target[targetMethodName] = origin[methodName].bind(origin);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} str
|
||||
* @returns
|
||||
*/
|
||||
encode (str) {
|
||||
let encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||
const string = String(str);
|
||||
let result = '';
|
||||
let currentIndex = 0;
|
||||
let sum = void 0;
|
||||
while (string.charAt(0 | currentIndex) || (encodings = '=', currentIndex % 1)) {
|
||||
currentIndex += 0.75;
|
||||
const currentCode = string.charCodeAt(currentIndex);
|
||||
if (currentCode > 255) {
|
||||
// Cannot handle when it is greater than 255
|
||||
throw new Error('"btoa" failed');
|
||||
}
|
||||
sum = sum << 8 | currentCode;
|
||||
const encodeIndex = 63 & sum >> 8 - currentIndex % 1 * 8;
|
||||
result += encodings.charAt(encodeIndex);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} str
|
||||
*/
|
||||
decode (str) {
|
||||
const encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||
let res = '';
|
||||
const string = String(str).replace(/[=]+$/, '');
|
||||
let o;
|
||||
let r;
|
||||
let i = 0;
|
||||
let currentIndex = 0;
|
||||
while (r = string.charAt(currentIndex)) {
|
||||
currentIndex += 1;
|
||||
r = encodings.indexOf(r);
|
||||
if (~r) {
|
||||
o = i % 4 ? 64 * o + r : r;
|
||||
if (i++ % 4) {
|
||||
res += String.fromCharCode(255 & o >> (-2 * i & 6));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {ArrayBuffer} buffer
|
||||
*/
|
||||
arrayBufferToBase64 (buffer) {
|
||||
return utils.encode(utils.arrayBufferToString(buffer));
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} base64
|
||||
*/
|
||||
base64ToArrayBuffer (base64) {
|
||||
return utils.stringToArrayBuffer(utils.decode(base64));
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {ArrayBuffer} buffer
|
||||
*/
|
||||
arrayBufferToString (buffer) {
|
||||
let result = '';
|
||||
const uintArray = new Uint8Array(buffer);
|
||||
const byteLength = uintArray.byteLength;
|
||||
for (let i = 0; i < byteLength; i++) {
|
||||
result += String.fromCharCode(uintArray[i]);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} string
|
||||
*/
|
||||
stringToArrayBuffer (string) {
|
||||
const length = string.length;
|
||||
const uintArray = new Uint8Array(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
uintArray[i] = string.charCodeAt(i);
|
||||
}
|
||||
return uintArray.buffer;
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = utils;
|
252
adapters/common/xmldom/dom-parser.js
Normal file
252
adapters/common/xmldom/dom-parser.js
Normal file
@@ -0,0 +1,252 @@
|
||||
function DOMParser(options){
|
||||
this.options = options ||{locator:{}};
|
||||
|
||||
}
|
||||
|
||||
DOMParser.prototype.parseFromString = function(source,mimeType){
|
||||
var options = this.options;
|
||||
var sax = new XMLReader();
|
||||
var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler
|
||||
var errorHandler = options.errorHandler;
|
||||
var locator = options.locator;
|
||||
var defaultNSMap = options.xmlns||{};
|
||||
var isHTML = /\/x?html?$/.test(mimeType);//mimeType.toLowerCase().indexOf('html') > -1;
|
||||
var entityMap = isHTML?htmlEntity.entityMap:{'lt':'<','gt':'>','amp':'&','quot':'"','apos':"'"};
|
||||
if(locator){
|
||||
domBuilder.setDocumentLocator(locator)
|
||||
}
|
||||
|
||||
sax.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator);
|
||||
sax.domBuilder = options.domBuilder || domBuilder;
|
||||
if(isHTML){
|
||||
defaultNSMap['']= 'http://www.w3.org/1999/xhtml';
|
||||
}
|
||||
defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace';
|
||||
if(source){
|
||||
sax.parse(source,defaultNSMap,entityMap);
|
||||
}else{
|
||||
sax.errorHandler.error("invalid doc source");
|
||||
}
|
||||
return domBuilder.doc;
|
||||
}
|
||||
function buildErrorHandler(errorImpl,domBuilder,locator){
|
||||
if(!errorImpl){
|
||||
if(domBuilder instanceof DOMHandler){
|
||||
return domBuilder;
|
||||
}
|
||||
errorImpl = domBuilder ;
|
||||
}
|
||||
var errorHandler = {}
|
||||
var isCallback = errorImpl instanceof Function;
|
||||
locator = locator||{}
|
||||
function build(key){
|
||||
var fn = errorImpl[key];
|
||||
if(!fn && isCallback){
|
||||
fn = errorImpl.length == 2?function(msg){errorImpl(key,msg)}:errorImpl;
|
||||
}
|
||||
errorHandler[key] = fn && function(msg){
|
||||
fn('[xmldom '+key+']\t'+msg+_locator(locator));
|
||||
}||function(){};
|
||||
}
|
||||
build('warning');
|
||||
build('error');
|
||||
build('fatalError');
|
||||
return errorHandler;
|
||||
}
|
||||
|
||||
//console.log('#\n\n\n\n\n\n\n####')
|
||||
/**
|
||||
* +ContentHandler+ErrorHandler
|
||||
* +LexicalHandler+EntityResolver2
|
||||
* -DeclHandler-DTDHandler
|
||||
*
|
||||
* DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler
|
||||
* DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2
|
||||
* @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html
|
||||
*/
|
||||
function DOMHandler() {
|
||||
this.cdata = false;
|
||||
}
|
||||
function position(locator,node){
|
||||
node.lineNumber = locator.lineNumber;
|
||||
node.columnNumber = locator.columnNumber;
|
||||
}
|
||||
/**
|
||||
* @see org.xml.sax.ContentHandler#startDocument
|
||||
* @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
|
||||
*/
|
||||
DOMHandler.prototype = {
|
||||
startDocument : function() {
|
||||
this.doc = new DOMImplementation().createDocument(null, null, null);
|
||||
if (this.locator) {
|
||||
this.doc.documentURI = this.locator.systemId;
|
||||
}
|
||||
},
|
||||
startElement:function(namespaceURI, localName, qName, attrs) {
|
||||
var doc = this.doc;
|
||||
var el = doc.createElementNS(namespaceURI, qName||localName);
|
||||
var len = attrs.length;
|
||||
appendElement(this, el);
|
||||
this.currentElement = el;
|
||||
|
||||
this.locator && position(this.locator,el)
|
||||
for (var i = 0 ; i < len; i++) {
|
||||
var namespaceURI = attrs.getURI(i);
|
||||
var value = attrs.getValue(i);
|
||||
var qName = attrs.getQName(i);
|
||||
var attr = doc.createAttributeNS(namespaceURI, qName);
|
||||
this.locator &&position(attrs.getLocator(i),attr);
|
||||
attr.value = attr.nodeValue = value;
|
||||
el.setAttributeNode(attr)
|
||||
}
|
||||
},
|
||||
endElement:function(namespaceURI, localName, qName) {
|
||||
var current = this.currentElement
|
||||
var tagName = current.tagName;
|
||||
this.currentElement = current.parentNode;
|
||||
},
|
||||
startPrefixMapping:function(prefix, uri) {
|
||||
},
|
||||
endPrefixMapping:function(prefix) {
|
||||
},
|
||||
processingInstruction:function(target, data) {
|
||||
var ins = this.doc.createProcessingInstruction(target, data);
|
||||
this.locator && position(this.locator,ins)
|
||||
appendElement(this, ins);
|
||||
},
|
||||
ignorableWhitespace:function(ch, start, length) {
|
||||
},
|
||||
characters:function(chars, start, length) {
|
||||
chars = _toString.apply(this,arguments)
|
||||
//console.log(chars)
|
||||
if(chars){
|
||||
if (this.cdata) {
|
||||
var charNode = this.doc.createCDATASection(chars);
|
||||
} else {
|
||||
var charNode = this.doc.createTextNode(chars);
|
||||
}
|
||||
if(this.currentElement){
|
||||
this.currentElement.appendChild(charNode);
|
||||
}else if(/^\s*$/.test(chars)){
|
||||
this.doc.appendChild(charNode);
|
||||
//process xml
|
||||
}
|
||||
this.locator && position(this.locator,charNode)
|
||||
}
|
||||
},
|
||||
skippedEntity:function(name) {
|
||||
},
|
||||
endDocument:function() {
|
||||
this.doc.normalize();
|
||||
},
|
||||
setDocumentLocator:function (locator) {
|
||||
if(this.locator = locator){// && !('lineNumber' in locator)){
|
||||
locator.lineNumber = 0;
|
||||
}
|
||||
},
|
||||
//LexicalHandler
|
||||
comment:function(chars, start, length) {
|
||||
chars = _toString.apply(this,arguments)
|
||||
var comm = this.doc.createComment(chars);
|
||||
this.locator && position(this.locator,comm)
|
||||
appendElement(this, comm);
|
||||
},
|
||||
|
||||
startCDATA:function() {
|
||||
//used in characters() methods
|
||||
this.cdata = true;
|
||||
},
|
||||
endCDATA:function() {
|
||||
this.cdata = false;
|
||||
},
|
||||
|
||||
startDTD:function(name, publicId, systemId) {
|
||||
var impl = this.doc.implementation;
|
||||
if (impl && impl.createDocumentType) {
|
||||
var dt = impl.createDocumentType(name, publicId, systemId);
|
||||
this.locator && position(this.locator,dt)
|
||||
appendElement(this, dt);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @see org.xml.sax.ErrorHandler
|
||||
* @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
|
||||
*/
|
||||
warning:function(error) {
|
||||
console.warn('[xmldom warning]\t'+error,_locator(this.locator));
|
||||
},
|
||||
error:function(error) {
|
||||
console.error('[xmldom error]\t'+error,_locator(this.locator));
|
||||
},
|
||||
fatalError:function(error) {
|
||||
console.error('[xmldom fatalError]\t'+error,_locator(this.locator));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
function _locator(l){
|
||||
if(l){
|
||||
return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']'
|
||||
}
|
||||
}
|
||||
function _toString(chars,start,length){
|
||||
if(typeof chars == 'string'){
|
||||
return chars.substr(start,length)
|
||||
}else{//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
|
||||
if(chars.length >= start+length || start){
|
||||
return new java.lang.String(chars,start,length)+'';
|
||||
}
|
||||
return chars;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html
|
||||
* used method of org.xml.sax.ext.LexicalHandler:
|
||||
* #comment(chars, start, length)
|
||||
* #startCDATA()
|
||||
* #endCDATA()
|
||||
* #startDTD(name, publicId, systemId)
|
||||
*
|
||||
*
|
||||
* IGNORED method of org.xml.sax.ext.LexicalHandler:
|
||||
* #endDTD()
|
||||
* #startEntity(name)
|
||||
* #endEntity(name)
|
||||
*
|
||||
*
|
||||
* @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html
|
||||
* IGNORED method of org.xml.sax.ext.DeclHandler
|
||||
* #attributeDecl(eName, aName, type, mode, value)
|
||||
* #elementDecl(name, model)
|
||||
* #externalEntityDecl(name, publicId, systemId)
|
||||
* #internalEntityDecl(name, value)
|
||||
* @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html
|
||||
* IGNORED method of org.xml.sax.EntityResolver2
|
||||
* #resolveEntity(String name,String publicId,String baseURI,String systemId)
|
||||
* #resolveEntity(publicId, systemId)
|
||||
* #getExternalSubset(name, baseURI)
|
||||
* @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html
|
||||
* IGNORED method of org.xml.sax.DTDHandler
|
||||
* #notationDecl(name, publicId, systemId) {};
|
||||
* #unparsedEntityDecl(name, publicId, systemId, notationName) {};
|
||||
*/
|
||||
"endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){
|
||||
DOMHandler.prototype[key] = function(){return null}
|
||||
})
|
||||
|
||||
/* Private static helpers treated below as private instance methods, so don't need to add these to the public API; we might use a Relator to also get rid of non-standard public properties */
|
||||
function appendElement (hander,node) {
|
||||
if (!hander.currentElement) {
|
||||
hander.doc.appendChild(node);
|
||||
} else {
|
||||
hander.currentElement.appendChild(node);
|
||||
}
|
||||
}//appendChild and setAttributeNS are preformance key
|
||||
|
||||
//if(typeof require == 'function'){
|
||||
var htmlEntity = require('./entities');
|
||||
var XMLReader = require('./sax').XMLReader;
|
||||
var DOMImplementation = exports.DOMImplementation = require('./dom').DOMImplementation;
|
||||
exports.XMLSerializer = require('./dom').XMLSerializer ;
|
||||
exports.DOMParser = DOMParser;
|
||||
//}
|
1246
adapters/common/xmldom/dom.js
Executable file
1246
adapters/common/xmldom/dom.js
Executable file
File diff suppressed because it is too large
Load Diff
244
adapters/common/xmldom/entities.js
Normal file
244
adapters/common/xmldom/entities.js
Normal file
@@ -0,0 +1,244 @@
|
||||
exports.entityMap = {
|
||||
lt: '<',
|
||||
gt: '>',
|
||||
amp: '&',
|
||||
quot: '"',
|
||||
apos: "'",
|
||||
Agrave: "À",
|
||||
Aacute: "Á",
|
||||
Acirc: "Â",
|
||||
Atilde: "Ã",
|
||||
Auml: "Ä",
|
||||
Aring: "Å",
|
||||
AElig: "Æ",
|
||||
Ccedil: "Ç",
|
||||
Egrave: "È",
|
||||
Eacute: "É",
|
||||
Ecirc: "Ê",
|
||||
Euml: "Ë",
|
||||
Igrave: "Ì",
|
||||
Iacute: "Í",
|
||||
Icirc: "Î",
|
||||
Iuml: "Ï",
|
||||
ETH: "Ð",
|
||||
Ntilde: "Ñ",
|
||||
Ograve: "Ò",
|
||||
Oacute: "Ó",
|
||||
Ocirc: "Ô",
|
||||
Otilde: "Õ",
|
||||
Ouml: "Ö",
|
||||
Oslash: "Ø",
|
||||
Ugrave: "Ù",
|
||||
Uacute: "Ú",
|
||||
Ucirc: "Û",
|
||||
Uuml: "Ü",
|
||||
Yacute: "Ý",
|
||||
THORN: "Þ",
|
||||
szlig: "ß",
|
||||
agrave: "à",
|
||||
aacute: "á",
|
||||
acirc: "â",
|
||||
atilde: "ã",
|
||||
auml: "ä",
|
||||
aring: "å",
|
||||
aelig: "æ",
|
||||
ccedil: "ç",
|
||||
egrave: "è",
|
||||
eacute: "é",
|
||||
ecirc: "ê",
|
||||
euml: "ë",
|
||||
igrave: "ì",
|
||||
iacute: "í",
|
||||
icirc: "î",
|
||||
iuml: "ï",
|
||||
eth: "ð",
|
||||
ntilde: "ñ",
|
||||
ograve: "ò",
|
||||
oacute: "ó",
|
||||
ocirc: "ô",
|
||||
otilde: "õ",
|
||||
ouml: "ö",
|
||||
oslash: "ø",
|
||||
ugrave: "ù",
|
||||
uacute: "ú",
|
||||
ucirc: "û",
|
||||
uuml: "ü",
|
||||
yacute: "ý",
|
||||
thorn: "þ",
|
||||
yuml: "ÿ",
|
||||
nbsp: " ",
|
||||
iexcl: "¡",
|
||||
cent: "¢",
|
||||
pound: "£",
|
||||
curren: "¤",
|
||||
yen: "¥",
|
||||
brvbar: "¦",
|
||||
sect: "§",
|
||||
uml: "¨",
|
||||
copy: "©",
|
||||
ordf: "ª",
|
||||
laquo: "«",
|
||||
not: "¬",
|
||||
shy: "",
|
||||
reg: "®",
|
||||
macr: "¯",
|
||||
deg: "°",
|
||||
plusmn: "±",
|
||||
sup2: "²",
|
||||
sup3: "³",
|
||||
acute: "´",
|
||||
micro: "µ",
|
||||
para: "¶",
|
||||
middot: "·",
|
||||
cedil: "¸",
|
||||
sup1: "¹",
|
||||
ordm: "º",
|
||||
raquo: "»",
|
||||
frac14: "¼",
|
||||
frac12: "½",
|
||||
frac34: "¾",
|
||||
iquest: "¿",
|
||||
times: "×",
|
||||
divide: "÷",
|
||||
forall: "∀",
|
||||
part: "∂",
|
||||
exist: "∃",
|
||||
empty: "∅",
|
||||
nabla: "∇",
|
||||
isin: "∈",
|
||||
notin: "∉",
|
||||
ni: "∋",
|
||||
prod: "∏",
|
||||
sum: "∑",
|
||||
minus: "−",
|
||||
lowast: "∗",
|
||||
radic: "√",
|
||||
prop: "∝",
|
||||
infin: "∞",
|
||||
ang: "∠",
|
||||
and: "∧",
|
||||
or: "∨",
|
||||
cap: "∩",
|
||||
cup: "∪",
|
||||
'int': "∫",
|
||||
there4: "∴",
|
||||
sim: "∼",
|
||||
cong: "≅",
|
||||
asymp: "≈",
|
||||
ne: "≠",
|
||||
equiv: "≡",
|
||||
le: "≤",
|
||||
ge: "≥",
|
||||
sub: "⊂",
|
||||
sup: "⊃",
|
||||
nsub: "⊄",
|
||||
sube: "⊆",
|
||||
supe: "⊇",
|
||||
oplus: "⊕",
|
||||
otimes: "⊗",
|
||||
perp: "⊥",
|
||||
sdot: "⋅",
|
||||
Alpha: "Α",
|
||||
Beta: "Β",
|
||||
Gamma: "Γ",
|
||||
Delta: "Δ",
|
||||
Epsilon: "Ε",
|
||||
Zeta: "Ζ",
|
||||
Eta: "Η",
|
||||
Theta: "Θ",
|
||||
Iota: "Ι",
|
||||
Kappa: "Κ",
|
||||
Lambda: "Λ",
|
||||
Mu: "Μ",
|
||||
Nu: "Ν",
|
||||
Xi: "Ξ",
|
||||
Omicron: "Ο",
|
||||
Pi: "Π",
|
||||
Rho: "Ρ",
|
||||
Sigma: "Σ",
|
||||
Tau: "Τ",
|
||||
Upsilon: "Υ",
|
||||
Phi: "Φ",
|
||||
Chi: "Χ",
|
||||
Psi: "Ψ",
|
||||
Omega: "Ω",
|
||||
alpha: "α",
|
||||
beta: "β",
|
||||
gamma: "γ",
|
||||
delta: "δ",
|
||||
epsilon: "ε",
|
||||
zeta: "ζ",
|
||||
eta: "η",
|
||||
theta: "θ",
|
||||
iota: "ι",
|
||||
kappa: "κ",
|
||||
lambda: "λ",
|
||||
mu: "μ",
|
||||
nu: "ν",
|
||||
xi: "ξ",
|
||||
omicron: "ο",
|
||||
pi: "π",
|
||||
rho: "ρ",
|
||||
sigmaf: "ς",
|
||||
sigma: "σ",
|
||||
tau: "τ",
|
||||
upsilon: "υ",
|
||||
phi: "φ",
|
||||
chi: "χ",
|
||||
psi: "ψ",
|
||||
omega: "ω",
|
||||
thetasym: "ϑ",
|
||||
upsih: "ϒ",
|
||||
piv: "ϖ",
|
||||
OElig: "Œ",
|
||||
oelig: "œ",
|
||||
Scaron: "Š",
|
||||
scaron: "š",
|
||||
Yuml: "Ÿ",
|
||||
fnof: "ƒ",
|
||||
circ: "ˆ",
|
||||
tilde: "˜",
|
||||
ensp: " ",
|
||||
emsp: " ",
|
||||
thinsp: " ",
|
||||
zwnj: "",
|
||||
zwj: "",
|
||||
lrm: "",
|
||||
rlm: "",
|
||||
ndash: "–",
|
||||
mdash: "—",
|
||||
lsquo: "‘",
|
||||
rsquo: "’",
|
||||
sbquo: "‚",
|
||||
ldquo: "“",
|
||||
rdquo: "”",
|
||||
bdquo: "„",
|
||||
dagger: "†",
|
||||
Dagger: "‡",
|
||||
bull: "•",
|
||||
hellip: "…",
|
||||
permil: "‰",
|
||||
prime: "′",
|
||||
Prime: "″",
|
||||
lsaquo: "‹",
|
||||
rsaquo: "›",
|
||||
oline: "‾",
|
||||
euro: "€",
|
||||
trade: "™",
|
||||
larr: "←",
|
||||
uarr: "↑",
|
||||
rarr: "→",
|
||||
darr: "↓",
|
||||
harr: "↔",
|
||||
crarr: "↵",
|
||||
lceil: "⌈",
|
||||
rceil: "⌉",
|
||||
lfloor: "⌊",
|
||||
rfloor: "⌋",
|
||||
loz: "◊",
|
||||
spades: "♠",
|
||||
clubs: "♣",
|
||||
hearts: "♥",
|
||||
diams: "♦"
|
||||
};
|
||||
//for(var n in exports.entityMap){console.log(exports.entityMap[n].charCodeAt())}
|
616
adapters/common/xmldom/sax.js
Normal file
616
adapters/common/xmldom/sax.js
Normal file
@@ -0,0 +1,616 @@
|
||||
//[4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
|
||||
//[4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
|
||||
//[5] Name ::= NameStartChar (NameChar)*
|
||||
var nameStartChar = /[A-Z_a-z\xC0-\xD6\xD8-\xF6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]///\u10000-\uEFFFF
|
||||
var nameChar = new RegExp("[\\-\\.0-9"+nameStartChar.source.slice(1,-1)+"\\u00B7\\u0300-\\u036F\\u203F-\\u2040]");
|
||||
var tagNamePattern = new RegExp('^'+nameStartChar.source+nameChar.source+'*(?:\:'+nameStartChar.source+nameChar.source+'*)?$');
|
||||
//var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/
|
||||
//var handlers = 'resolveEntity,getExternalSubset,characters,endDocument,endElement,endPrefixMapping,ignorableWhitespace,processingInstruction,setDocumentLocator,skippedEntity,startDocument,startElement,startPrefixMapping,notationDecl,unparsedEntityDecl,error,fatalError,warning,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,comment,endCDATA,endDTD,endEntity,startCDATA,startDTD,startEntity'.split(',')
|
||||
|
||||
//S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE
|
||||
//S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE
|
||||
var S_TAG = 0;//tag name offerring
|
||||
var S_ATTR = 1;//attr name offerring
|
||||
var S_ATTR_SPACE=2;//attr name end and space offer
|
||||
var S_EQ = 3;//=space?
|
||||
var S_ATTR_NOQUOT_VALUE = 4;//attr value(no quot value only)
|
||||
var S_ATTR_END = 5;//attr value end and no space(quot end)
|
||||
var S_TAG_SPACE = 6;//(attr value end || tag end ) && (space offer)
|
||||
var S_TAG_CLOSE = 7;//closed el<el />
|
||||
|
||||
function XMLReader(){
|
||||
|
||||
}
|
||||
|
||||
XMLReader.prototype = {
|
||||
parse:function(source,defaultNSMap,entityMap){
|
||||
var domBuilder = this.domBuilder;
|
||||
domBuilder.startDocument();
|
||||
_copy(defaultNSMap ,defaultNSMap = {})
|
||||
parse(source,defaultNSMap,entityMap,
|
||||
domBuilder,this.errorHandler);
|
||||
domBuilder.endDocument();
|
||||
}
|
||||
}
|
||||
function parse(source,defaultNSMapCopy,entityMap,domBuilder,errorHandler){
|
||||
function fixedFromCharCode(code) {
|
||||
// String.prototype.fromCharCode does not supports
|
||||
// > 2 bytes unicode chars directly
|
||||
if (code > 0xffff) {
|
||||
code -= 0x10000;
|
||||
var surrogate1 = 0xd800 + (code >> 10)
|
||||
, surrogate2 = 0xdc00 + (code & 0x3ff);
|
||||
|
||||
return String.fromCharCode(surrogate1, surrogate2);
|
||||
} else {
|
||||
return String.fromCharCode(code);
|
||||
}
|
||||
}
|
||||
function entityReplacer(a){
|
||||
var k = a.slice(1,-1);
|
||||
if(k in entityMap){
|
||||
return entityMap[k];
|
||||
}else if(k.charAt(0) === '#'){
|
||||
return fixedFromCharCode(parseInt(k.substr(1).replace('x','0x')))
|
||||
}else{
|
||||
errorHandler.error('entity not found:'+a);
|
||||
return a;
|
||||
}
|
||||
}
|
||||
function appendText(end){//has some bugs
|
||||
if(end>start){
|
||||
var xt = source.substring(start,end).replace(/&#?\w+;/g,entityReplacer);
|
||||
locator&&position(start);
|
||||
domBuilder.characters(xt,0,end-start);
|
||||
start = end
|
||||
}
|
||||
}
|
||||
function position(p,m){
|
||||
while(p>=lineEnd && (m = linePattern.exec(source))){
|
||||
lineStart = m.index;
|
||||
lineEnd = lineStart + m[0].length;
|
||||
locator.lineNumber++;
|
||||
//console.log('line++:',locator,startPos,endPos)
|
||||
}
|
||||
locator.columnNumber = p-lineStart+1;
|
||||
}
|
||||
var lineStart = 0;
|
||||
var lineEnd = 0;
|
||||
var linePattern = /.*(?:\r\n?|\n)|.*$/g
|
||||
var locator = domBuilder.locator;
|
||||
|
||||
var parseStack = [{currentNSMap:defaultNSMapCopy}]
|
||||
var closeMap = {};
|
||||
var start = 0;
|
||||
while(true){
|
||||
try{
|
||||
var tagStart = source.indexOf('<',start);
|
||||
if(tagStart<0){
|
||||
if(!source.substr(start).match(/^\s*$/)){
|
||||
var doc = domBuilder.doc;
|
||||
var text = doc.createTextNode(source.substr(start));
|
||||
doc.appendChild(text);
|
||||
domBuilder.currentElement = text;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(tagStart>start){
|
||||
appendText(tagStart);
|
||||
}
|
||||
switch(source.charAt(tagStart+1)){
|
||||
case '/':
|
||||
var end = source.indexOf('>',tagStart+3);
|
||||
var tagName = source.substring(tagStart+2,end);
|
||||
var config = parseStack.pop();
|
||||
if(end<0){
|
||||
|
||||
tagName = source.substring(tagStart+2).replace(/[\s<].*/,'');
|
||||
//console.error('#@@@@@@'+tagName)
|
||||
errorHandler.error("end tag name: "+tagName+' is not complete:'+config.tagName);
|
||||
end = tagStart+1+tagName.length;
|
||||
}else if(tagName.match(/\s</)){
|
||||
tagName = tagName.replace(/[\s<].*/,'');
|
||||
errorHandler.error("end tag name: "+tagName+' maybe not complete');
|
||||
end = tagStart+1+tagName.length;
|
||||
}
|
||||
//console.error(parseStack.length,parseStack)
|
||||
//console.error(config);
|
||||
var localNSMap = config.localNSMap;
|
||||
var endMatch = config.tagName == tagName;
|
||||
var endIgnoreCaseMach = endMatch || config.tagName&&config.tagName.toLowerCase() == tagName.toLowerCase()
|
||||
if(endIgnoreCaseMach){
|
||||
domBuilder.endElement(config.uri,config.localName,tagName);
|
||||
if(localNSMap){
|
||||
for(var prefix in localNSMap){
|
||||
domBuilder.endPrefixMapping(prefix) ;
|
||||
}
|
||||
}
|
||||
if(!endMatch){
|
||||
errorHandler.fatalError("end tag name: "+tagName+' is not match the current start tagName:'+config.tagName );
|
||||
}
|
||||
}else{
|
||||
parseStack.push(config)
|
||||
}
|
||||
|
||||
end++;
|
||||
break;
|
||||
// end elment
|
||||
case '?':// <?...?>
|
||||
locator&&position(tagStart);
|
||||
end = parseInstruction(source,tagStart,domBuilder);
|
||||
break;
|
||||
case '!':// <!doctype,<![CDATA,<!--
|
||||
locator&&position(tagStart);
|
||||
end = parseDCC(source,tagStart,domBuilder,errorHandler);
|
||||
break;
|
||||
default:
|
||||
locator&&position(tagStart);
|
||||
var el = new ElementAttributes();
|
||||
var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
|
||||
//elStartEnd
|
||||
var end = parseElementStartPart(source,tagStart,el,currentNSMap,entityReplacer,errorHandler);
|
||||
var len = el.length;
|
||||
|
||||
|
||||
if(!el.closed && fixSelfClosed(source,end,el.tagName,closeMap)){
|
||||
el.closed = true;
|
||||
if(!entityMap.nbsp){
|
||||
errorHandler.warning('unclosed xml attribute');
|
||||
}
|
||||
}
|
||||
if(locator && len){
|
||||
var locator2 = copyLocator(locator,{});
|
||||
//try{//attribute position fixed
|
||||
for(var i = 0;i<len;i++){
|
||||
var a = el[i];
|
||||
position(a.offset);
|
||||
a.locator = copyLocator(locator,{});
|
||||
}
|
||||
//}catch(e){console.error('@@@@@'+e)}
|
||||
domBuilder.locator = locator2
|
||||
if(appendElement(el,domBuilder,currentNSMap)){
|
||||
parseStack.push(el)
|
||||
}
|
||||
domBuilder.locator = locator;
|
||||
}else{
|
||||
if(appendElement(el,domBuilder,currentNSMap)){
|
||||
parseStack.push(el)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(el.uri === 'http://www.w3.org/1999/xhtml' && !el.closed){
|
||||
end = parseHtmlSpecialContent(source,end,el.tagName,entityReplacer,domBuilder)
|
||||
}else{
|
||||
end++;
|
||||
}
|
||||
}
|
||||
}catch(e){
|
||||
errorHandler.error('element parse error: '+e)
|
||||
//errorHandler.error('element parse error: '+e);
|
||||
end = -1;
|
||||
//throw e;
|
||||
}
|
||||
if(end>start){
|
||||
start = end;
|
||||
}else{
|
||||
//TODO: 这里有可能sax回退,有位置错误风险
|
||||
appendText(Math.max(tagStart,start)+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
function copyLocator(f,t){
|
||||
t.lineNumber = f.lineNumber;
|
||||
t.columnNumber = f.columnNumber;
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack);
|
||||
* @return end of the elementStartPart(end of elementEndPart for selfClosed el)
|
||||
*/
|
||||
function parseElementStartPart(source,start,el,currentNSMap,entityReplacer,errorHandler){
|
||||
var attrName;
|
||||
var value;
|
||||
var p = ++start;
|
||||
var s = S_TAG;//status
|
||||
while(true){
|
||||
var c = source.charAt(p);
|
||||
switch(c){
|
||||
case '=':
|
||||
if(s === S_ATTR){//attrName
|
||||
attrName = source.slice(start,p);
|
||||
s = S_EQ;
|
||||
}else if(s === S_ATTR_SPACE){
|
||||
s = S_EQ;
|
||||
}else{
|
||||
//fatalError: equal must after attrName or space after attrName
|
||||
throw new Error('attribute equal must after attrName');
|
||||
}
|
||||
break;
|
||||
case '\'':
|
||||
case '"':
|
||||
if(s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE
|
||||
){//equal
|
||||
if(s === S_ATTR){
|
||||
errorHandler.warning('attribute value must after "="')
|
||||
attrName = source.slice(start,p)
|
||||
}
|
||||
start = p+1;
|
||||
p = source.indexOf(c,start)
|
||||
if(p>0){
|
||||
value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
|
||||
el.add(attrName,value,start-1);
|
||||
s = S_ATTR_END;
|
||||
}else{
|
||||
//fatalError: no end quot match
|
||||
throw new Error('attribute value no end \''+c+'\' match');
|
||||
}
|
||||
}else if(s == S_ATTR_NOQUOT_VALUE){
|
||||
value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
|
||||
//console.log(attrName,value,start,p)
|
||||
el.add(attrName,value,start);
|
||||
//console.dir(el)
|
||||
errorHandler.warning('attribute "'+attrName+'" missed start quot('+c+')!!');
|
||||
start = p+1;
|
||||
s = S_ATTR_END
|
||||
}else{
|
||||
//fatalError: no equal before
|
||||
throw new Error('attribute value must after "="');
|
||||
}
|
||||
break;
|
||||
case '/':
|
||||
switch(s){
|
||||
case S_TAG:
|
||||
el.setTagName(source.slice(start,p));
|
||||
case S_ATTR_END:
|
||||
case S_TAG_SPACE:
|
||||
case S_TAG_CLOSE:
|
||||
s =S_TAG_CLOSE;
|
||||
el.closed = true;
|
||||
case S_ATTR_NOQUOT_VALUE:
|
||||
case S_ATTR:
|
||||
case S_ATTR_SPACE:
|
||||
break;
|
||||
//case S_EQ:
|
||||
default:
|
||||
throw new Error("attribute invalid close char('/')")
|
||||
}
|
||||
break;
|
||||
case ''://end document
|
||||
//throw new Error('unexpected end of input')
|
||||
errorHandler.error('unexpected end of input');
|
||||
if(s == S_TAG){
|
||||
el.setTagName(source.slice(start,p));
|
||||
}
|
||||
return p;
|
||||
case '>':
|
||||
switch(s){
|
||||
case S_TAG:
|
||||
el.setTagName(source.slice(start,p));
|
||||
case S_ATTR_END:
|
||||
case S_TAG_SPACE:
|
||||
case S_TAG_CLOSE:
|
||||
break;//normal
|
||||
case S_ATTR_NOQUOT_VALUE://Compatible state
|
||||
case S_ATTR:
|
||||
value = source.slice(start,p);
|
||||
if(value.slice(-1) === '/'){
|
||||
el.closed = true;
|
||||
value = value.slice(0,-1)
|
||||
}
|
||||
case S_ATTR_SPACE:
|
||||
if(s === S_ATTR_SPACE){
|
||||
value = attrName;
|
||||
}
|
||||
if(s == S_ATTR_NOQUOT_VALUE){
|
||||
errorHandler.warning('attribute "'+value+'" missed quot(")!!');
|
||||
el.add(attrName,value.replace(/&#?\w+;/g,entityReplacer),start)
|
||||
}else{
|
||||
if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)){
|
||||
errorHandler.warning('attribute "'+value+'" missed value!! "'+value+'" instead!!')
|
||||
}
|
||||
el.add(value,value,start)
|
||||
}
|
||||
break;
|
||||
case S_EQ:
|
||||
throw new Error('attribute value missed!!');
|
||||
}
|
||||
// console.log(tagName,tagNamePattern,tagNamePattern.test(tagName))
|
||||
return p;
|
||||
/*xml space '\x20' | #x9 | #xD | #xA; */
|
||||
case '\u0080':
|
||||
c = ' ';
|
||||
default:
|
||||
if(c<= ' '){//space
|
||||
switch(s){
|
||||
case S_TAG:
|
||||
el.setTagName(source.slice(start,p));//tagName
|
||||
s = S_TAG_SPACE;
|
||||
break;
|
||||
case S_ATTR:
|
||||
attrName = source.slice(start,p)
|
||||
s = S_ATTR_SPACE;
|
||||
break;
|
||||
case S_ATTR_NOQUOT_VALUE:
|
||||
var value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
|
||||
errorHandler.warning('attribute "'+value+'" missed quot(")!!');
|
||||
el.add(attrName,value,start)
|
||||
case S_ATTR_END:
|
||||
s = S_TAG_SPACE;
|
||||
break;
|
||||
//case S_TAG_SPACE:
|
||||
//case S_EQ:
|
||||
//case S_ATTR_SPACE:
|
||||
// void();break;
|
||||
//case S_TAG_CLOSE:
|
||||
//ignore warning
|
||||
}
|
||||
}else{//not space
|
||||
//S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE
|
||||
//S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE
|
||||
switch(s){
|
||||
//case S_TAG:void();break;
|
||||
//case S_ATTR:void();break;
|
||||
//case S_ATTR_NOQUOT_VALUE:void();break;
|
||||
case S_ATTR_SPACE:
|
||||
var tagName = el.tagName;
|
||||
if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)){
|
||||
errorHandler.warning('attribute "'+attrName+'" missed value!! "'+attrName+'" instead2!!')
|
||||
}
|
||||
el.add(attrName,attrName,start);
|
||||
start = p;
|
||||
s = S_ATTR;
|
||||
break;
|
||||
case S_ATTR_END:
|
||||
errorHandler.warning('attribute space is required"'+attrName+'"!!')
|
||||
case S_TAG_SPACE:
|
||||
s = S_ATTR;
|
||||
start = p;
|
||||
break;
|
||||
case S_EQ:
|
||||
s = S_ATTR_NOQUOT_VALUE;
|
||||
start = p;
|
||||
break;
|
||||
case S_TAG_CLOSE:
|
||||
throw new Error("elements closed character '/' and '>' must be connected to");
|
||||
}
|
||||
}
|
||||
}//end outer switch
|
||||
//console.log('p++',p)
|
||||
p++;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return true if has new namespace define
|
||||
*/
|
||||
function appendElement(el,domBuilder,currentNSMap){
|
||||
var tagName = el.tagName;
|
||||
var localNSMap = null;
|
||||
//var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
|
||||
var i = el.length;
|
||||
while(i--){
|
||||
var a = el[i];
|
||||
var qName = a.qName;
|
||||
var value = a.value;
|
||||
var nsp = qName.indexOf(':');
|
||||
if(nsp>0){
|
||||
var prefix = a.prefix = qName.slice(0,nsp);
|
||||
var localName = qName.slice(nsp+1);
|
||||
var nsPrefix = prefix === 'xmlns' && localName
|
||||
}else{
|
||||
localName = qName;
|
||||
prefix = null
|
||||
nsPrefix = qName === 'xmlns' && ''
|
||||
}
|
||||
//can not set prefix,because prefix !== ''
|
||||
a.localName = localName ;
|
||||
//prefix == null for no ns prefix attribute
|
||||
if(nsPrefix !== false){//hack!!
|
||||
if(localNSMap == null){
|
||||
localNSMap = {}
|
||||
//console.log(currentNSMap,0)
|
||||
_copy(currentNSMap,currentNSMap={})
|
||||
//console.log(currentNSMap,1)
|
||||
}
|
||||
currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value;
|
||||
a.uri = 'http://www.w3.org/2000/xmlns/'
|
||||
domBuilder.startPrefixMapping(nsPrefix, value)
|
||||
}
|
||||
}
|
||||
var i = el.length;
|
||||
while(i--){
|
||||
a = el[i];
|
||||
var prefix = a.prefix;
|
||||
if(prefix){//no prefix attribute has no namespace
|
||||
if(prefix === 'xml'){
|
||||
a.uri = 'http://www.w3.org/XML/1998/namespace';
|
||||
}if(prefix !== 'xmlns'){
|
||||
a.uri = currentNSMap[prefix || '']
|
||||
|
||||
//{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)}
|
||||
}
|
||||
}
|
||||
}
|
||||
var nsp = tagName.indexOf(':');
|
||||
if(nsp>0){
|
||||
prefix = el.prefix = tagName.slice(0,nsp);
|
||||
localName = el.localName = tagName.slice(nsp+1);
|
||||
}else{
|
||||
prefix = null;//important!!
|
||||
localName = el.localName = tagName;
|
||||
}
|
||||
//no prefix element has default namespace
|
||||
var ns = el.uri = currentNSMap[prefix || ''];
|
||||
domBuilder.startElement(ns,localName,tagName,el);
|
||||
//endPrefixMapping and startPrefixMapping have not any help for dom builder
|
||||
//localNSMap = null
|
||||
if(el.closed){
|
||||
domBuilder.endElement(ns,localName,tagName);
|
||||
if(localNSMap){
|
||||
for(prefix in localNSMap){
|
||||
domBuilder.endPrefixMapping(prefix)
|
||||
}
|
||||
}
|
||||
}else{
|
||||
el.currentNSMap = currentNSMap;
|
||||
el.localNSMap = localNSMap;
|
||||
//parseStack.push(el);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
function parseHtmlSpecialContent(source,elStartEnd,tagName,entityReplacer,domBuilder){
|
||||
if(/^(?:script|textarea)$/i.test(tagName)){
|
||||
var elEndStart = source.indexOf('</'+tagName+'>',elStartEnd);
|
||||
var text = source.substring(elStartEnd+1,elEndStart);
|
||||
if(/[&<]/.test(text)){
|
||||
if(/^script$/i.test(tagName)){
|
||||
//if(!/\]\]>/.test(text)){
|
||||
//lexHandler.startCDATA();
|
||||
domBuilder.characters(text,0,text.length);
|
||||
//lexHandler.endCDATA();
|
||||
return elEndStart;
|
||||
//}
|
||||
}//}else{//text area
|
||||
text = text.replace(/&#?\w+;/g,entityReplacer);
|
||||
domBuilder.characters(text,0,text.length);
|
||||
return elEndStart;
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
return elStartEnd+1;
|
||||
}
|
||||
function fixSelfClosed(source,elStartEnd,tagName,closeMap){
|
||||
//if(tagName in closeMap){
|
||||
var pos = closeMap[tagName];
|
||||
if(pos == null){
|
||||
//console.log(tagName)
|
||||
pos = source.lastIndexOf('</'+tagName+'>')
|
||||
if(pos<elStartEnd){//忘记闭合
|
||||
pos = source.lastIndexOf('</'+tagName)
|
||||
}
|
||||
closeMap[tagName] =pos
|
||||
}
|
||||
return pos<elStartEnd;
|
||||
//}
|
||||
}
|
||||
function _copy(source,target){
|
||||
for(var n in source){target[n] = source[n]}
|
||||
}
|
||||
function parseDCC(source,start,domBuilder,errorHandler){//sure start with '<!'
|
||||
var next= source.charAt(start+2)
|
||||
switch(next){
|
||||
case '-':
|
||||
if(source.charAt(start + 3) === '-'){
|
||||
var end = source.indexOf('-->',start+4);
|
||||
//append comment source.substring(4,end)//<!--
|
||||
if(end>start){
|
||||
domBuilder.comment(source,start+4,end-start-4);
|
||||
return end+3;
|
||||
}else{
|
||||
errorHandler.error("Unclosed comment");
|
||||
return -1;
|
||||
}
|
||||
}else{
|
||||
//error
|
||||
return -1;
|
||||
}
|
||||
default:
|
||||
if(source.substr(start+3,6) == 'CDATA['){
|
||||
var end = source.indexOf(']]>',start+9);
|
||||
domBuilder.startCDATA();
|
||||
domBuilder.characters(source,start+9,end-start-9);
|
||||
domBuilder.endCDATA()
|
||||
return end+3;
|
||||
}
|
||||
//<!DOCTYPE
|
||||
//startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId)
|
||||
var matchs = split(source,start);
|
||||
var len = matchs.length;
|
||||
if(len>1 && /!doctype/i.test(matchs[0][0])){
|
||||
var name = matchs[1][0];
|
||||
var pubid = len>3 && /^public$/i.test(matchs[2][0]) && matchs[3][0]
|
||||
var sysid = len>4 && matchs[4][0];
|
||||
var lastMatch = matchs[len-1]
|
||||
domBuilder.startDTD(name,pubid && pubid.replace(/^(['"])(.*?)\1$/,'$2'),
|
||||
sysid && sysid.replace(/^(['"])(.*?)\1$/,'$2'));
|
||||
domBuilder.endDTD();
|
||||
|
||||
return lastMatch.index+lastMatch[0].length
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function parseInstruction(source,start,domBuilder){
|
||||
var end = source.indexOf('?>',start);
|
||||
if(end){
|
||||
var match = source.substring(start,end).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/);
|
||||
if(match){
|
||||
var len = match[0].length;
|
||||
domBuilder.processingInstruction(match[1], match[2]) ;
|
||||
return end+2;
|
||||
}else{//error
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param source
|
||||
*/
|
||||
function ElementAttributes(source){
|
||||
|
||||
}
|
||||
ElementAttributes.prototype = {
|
||||
setTagName:function(tagName){
|
||||
if(!tagNamePattern.test(tagName)){
|
||||
throw new Error('invalid tagName:'+tagName)
|
||||
}
|
||||
this.tagName = tagName
|
||||
},
|
||||
add:function(qName,value,offset){
|
||||
if(!tagNamePattern.test(qName)){
|
||||
throw new Error('invalid attribute:'+qName)
|
||||
}
|
||||
this[this.length++] = {qName:qName,value:value,offset:offset}
|
||||
},
|
||||
length:0,
|
||||
getLocalName:function(i){return this[i].localName},
|
||||
getLocator:function(i){return this[i].locator},
|
||||
getQName:function(i){return this[i].qName},
|
||||
getURI:function(i){return this[i].uri},
|
||||
getValue:function(i){return this[i].value}
|
||||
// ,getIndex:function(uri, localName)){
|
||||
// if(localName){
|
||||
//
|
||||
// }else{
|
||||
// var qName = uri
|
||||
// }
|
||||
// },
|
||||
// getValue:function(){return this.getValue(this.getIndex.apply(this,arguments))},
|
||||
// getType:function(uri,localName){}
|
||||
// getType:function(i){},
|
||||
}
|
||||
|
||||
|
||||
|
||||
function split(source,start){
|
||||
var match;
|
||||
var buf = [];
|
||||
var reg = /'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g;
|
||||
reg.lastIndex = start;
|
||||
reg.exec(source);//skip <
|
||||
while(match = reg.exec(source)){
|
||||
buf.push(match);
|
||||
if(match[1])return buf;
|
||||
}
|
||||
}
|
||||
|
||||
exports.XMLReader = XMLReader;
|
||||
|
Reference in New Issue
Block a user