[adapters] 增加小游戏适配部分源码

This commit is contained in:
SmallMain
2024-10-16 17:12:08 +08:00
parent 887d4a96c9
commit 07bf3b7a96
345 changed files with 38447 additions and 0 deletions

View 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;

View 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 || '';
};
}

View 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 () { },
});
}

View File

@@ -0,0 +1,3 @@
if (cc && cc.audioEngine) {
cc.audioEngine._maxAudioInstance = 10;
}

View 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();
},
});

View 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);
},
});
},
});
})();

View 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
});

View 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;
},
});
}

View File

@@ -0,0 +1,5 @@
Object.assign(cc.screen, {
autoFullScreen: function (element, onFullScreenChange) {
// Not support on mini game
}
});

View 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();
},
});
}

View 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;

View 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;

View 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;

View File

@@ -0,0 +1,9 @@
const adapter = window.__globalAdapter;
Object.assign(adapter, {
adaptSys: require('./BaseSystemInfo'),
adaptView: require('./View'),
adaptContainerStrategy: require('./ContainerStrategy'),
});

View 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');

View File

@@ -0,0 +1 @@
cc.macro.DOWNLOAD_MAX_CONCURRENT = 10;

111
adapters/common/utils.js Normal file
View 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;

View 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

File diff suppressed because it is too large Load Diff

View 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())}

View 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;