[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,205 @@
import HTMLAudioElement from './HTMLAudioElement'
const HAVE_NOTHING = 0
const HAVE_METADATA = 1
const HAVE_CURRENT_DATA = 2
const HAVE_FUTURE_DATA = 3
const HAVE_ENOUGH_DATA = 4
let SN_SEED = 1
const _innerAudioContextMap = {}
export default class Audio extends HTMLAudioElement {
constructor(url) {
super()
this._$sn = SN_SEED++;
this.HAVE_NOTHING = HAVE_NOTHING
this.HAVE_METADATA = HAVE_METADATA
this.HAVE_CURRENT_DATA = HAVE_CURRENT_DATA
this.HAVE_FUTURE_DATA = HAVE_FUTURE_DATA
this.HAVE_ENOUGH_DATA = HAVE_ENOUGH_DATA
this.readyState = HAVE_NOTHING
const innerAudioContext = my.createInnerAudioContext()
_innerAudioContextMap[this._$sn] = innerAudioContext
this._canplayEvents = [
'load',
'loadend',
'canplay',
'canplaythrough',
'loadedmetadata'
]
innerAudioContext.onCanplay(() => {
this._loaded = true
this.readyState = this.HAVE_CURRENT_DATA
this._canplayEvents.forEach((type) => {
this.dispatchEvent({ type: type })
})
})
innerAudioContext.onPlay(() => {
this._paused = _innerAudioContextMap[this._$sn].paused
this.dispatchEvent({ type: 'play' })
})
innerAudioContext.onPause(() => {
this._paused = _innerAudioContextMap[this._$sn].paused
this.dispatchEvent({ type: 'pause' })
})
innerAudioContext.onEnded(() => {
this._paused = _innerAudioContextMap[this._$sn].paused
if (_innerAudioContextMap[this._$sn].loop === false) {
this.dispatchEvent({ type: 'ended' })
}
this.readyState = HAVE_ENOUGH_DATA
})
innerAudioContext.onError(() => {
this._paused = _innerAudioContextMap[this._$sn].paused
this.dispatchEvent({ type: 'error' })
})
if (url) {
this.src = url
} else {
this._src = ''
}
this._loop = innerAudioContext.loop
this._autoplay = innerAudioContext.autoplay
this._paused = innerAudioContext.paused
this._volume = innerAudioContext.volume
this._muted = false;
}
addEventListener(type, listener, options = {}) {
super.addEventListener(type, listener, options)
type = String(type).toLowerCase()
if (this._loaded && this._canplayEvents.indexOf(type) !== -1) {
this.dispatchEvent({ type: type })
}
}
load() {
// console.warn('HTMLAudioElement.load() is not implemented.')
// weixin doesn't need call load() manually
}
play() {
_innerAudioContextMap[this._$sn].play()
}
resume() {
_innerAudioContextMap[this._$sn].resume()
}
pause() {
_innerAudioContextMap[this._$sn].pause()
}
stop() {
_innerAudioContextMap[this._$sn].stop()
}
destroy() {
_innerAudioContextMap[this._$sn].destroy()
}
canPlayType(mediaType = '') {
if (typeof mediaType !== 'string') {
return ''
}
if (mediaType.indexOf('audio/mpeg') > -1 || mediaType.indexOf('audio/mp4')) {
return 'probably'
}
return ''
}
get currentTime() {
return _innerAudioContextMap[this._$sn].currentTime
}
set currentTime(value) {
_innerAudioContextMap[this._$sn].seek(value)
}
get duration () {
return _innerAudioContextMap[this._$sn].duration
}
get src() {
return this._src
}
set src(value) {
this._src = value
this._loaded = false
this.readyState = this.HAVE_NOTHING
const innerAudioContext = _innerAudioContextMap[this._$sn]
innerAudioContext.src = value
}
get loop() {
return this._loop
}
set loop(value) {
this._loop = value
_innerAudioContextMap[this._$sn].loop = value
}
get autoplay() {
return this.autoplay
}
set autoplay(value) {
this._autoplay = value
_innerAudioContextMap[this._$sn].autoplay = value
}
get paused() {
return this._paused;
}
get volume() {
return this._volume;
}
set volume(value) {
this._volume = value;
if (!this._muted) {
_innerAudioContextMap[this._$sn].volume = value;
}
}
get muted() {
return this._muted;
}
set muted(value) {
this._muted = value;
if (value) {
_innerAudioContextMap[this._$sn].volume = 0;
} else {
_innerAudioContextMap[this._$sn].volume = this._volume;
}
}
cloneNode() {
const newAudio = new Audio()
newAudio.loop = this.loop
newAudio.autoplay = this.autoplay
newAudio.src = this.src
return newAudio
}
}

View File

@@ -0,0 +1,69 @@
import { innerWidth, innerHeight } from './WindowProperties'
function Canvas () {}
let CanvasProxy = new Proxy(Canvas, {
construct () {
const canvas = my.createOffscreenCanvas()
canvas.type = 'canvas'
// canvas.__proto__.__proto__.__proto__ = new HTMLCanvasElement()
const _getContext = canvas.getContext
canvas.getBoundingClientRect = () => {
const ret = {
top: 0,
left: 0,
width: window.innerWidth,
height: window.innerHeight
}
return ret
}
canvas.style = {
top: '0px',
left: '0px',
width: innerWidth + 'px',
height: innerHeight + 'px',
}
canvas.addEventListener = function (type, listener, options = {}) {
// console.log('canvas.addEventListener', type);
document.addEventListener(type, listener, options);
}
canvas.removeEventListener = function (type, listener) {
// console.log('canvas.removeEventListener', type);
document.removeEventListener(type, listener);
}
canvas.dispatchEvent = function (event = {}) {
console.log('canvas.dispatchEvent' , event.type, event);
// nothing to do
}
Object.defineProperty(canvas, 'clientWidth', {
enumerable: true,
get: function get() {
return innerWidth
}
})
Object.defineProperty(canvas, 'clientHeight', {
enumerable: true,
get: function get() {
return innerHeight
}
})
return canvas
},
});
// NOTE: this is a hack operation
// let canvas = new window.Canvas()
// console.error(canvas instanceof window.Canvas) => false
export default CanvasProxy;

View File

@@ -0,0 +1,10 @@
import Node from './Node'
export default class Element extends Node {
className = ''
children = []
constructor() {
super()
}
}

View File

@@ -0,0 +1,14 @@
import {noop} from './util'
export default class Event {
cancelBubble = false
cancelable = false
target = null
timestampe = Date.now()
preventDefault = noop
stopPropagation = noop
constructor(type) {
this.type = type
}
}

View File

@@ -0,0 +1,57 @@
const _events = new WeakMap()
export default class EventTarget {
constructor() {
_events.set(this, {})
}
addEventListener(type, listener, options = {}) {
let events = _events.get(this)
if (!events) {
events = {}
_events.set(this, events)
}
if (!events[type]) {
events[type] = []
}
events[type].push(listener)
if (options.capture) {
// console.warn('EventTarget.addEventListener: options.capture is not implemented.')
}
if (options.once) {
// console.warn('EventTarget.addEventListener: options.once is not implemented.')
}
if (options.passive) {
// console.warn('EventTarget.addEventListener: options.passive is not implemented.')
}
}
removeEventListener(type, listener) {
const events = _events.get(this)
if (events) {
const listeners = events[type]
if (listeners && listeners.length > 0) {
for (let i = listeners.length; i--; i > 0) {
if (listeners[i] === listener) {
listeners.splice(i, 1)
break
}
}
}
}
}
dispatchEvent(event = {}) {
const listeners = _events.get(this)[event.type]
if (listeners) {
for (let i = 0; i < listeners.length; i++) {
listeners[i](event)
}
}
}
}

View File

@@ -0,0 +1,7 @@
import HTMLElement from './HTMLElement'
export default class HTMLAudioElement extends HTMLElement {
constructor() {
super('audio')
}
}

View File

@@ -0,0 +1,3 @@
let HTMLCanvasElement = my.createOffscreenCanvas().constructor;
export default HTMLCanvasElement;

View File

@@ -0,0 +1,54 @@
import Element from './Element'
import { noop } from './util/index.js'
import { innerWidth, innerHeight } from './WindowProperties'
export default class HTMLElement extends Element {
className = ''
childern = []
style = {
width: `${innerWidth}px`,
height: `${innerHeight}px`
}
insertBefore = noop
innerHTML = ''
constructor(tagName = '') {
super()
this.tagName = tagName.toUpperCase()
}
setAttribute(name, value) {
this[name] = value
}
getAttribute(name) {
return this[name]
}
get clientWidth() {
const ret = parseInt(this.style.fontSize, 10) * this.innerHTML.length
return Number.isNaN(ret) ? 0 : ret
}
get clientHeight() {
const ret = parseInt(this.style.fontSize, 10)
return Number.isNaN(ret) ? 0 : ret
}
getBoundingClientRect() {
return {
top: 0,
left: 0,
width: innerWidth,
height: innerHeight
}
}
focus() {
}
}

View File

@@ -0,0 +1,4 @@
var screencanvas = $global.screencanvas;
let HTMLImageElement = screencanvas.createImage().constructor;
export default HTMLImageElement;

View File

@@ -0,0 +1,40 @@
// var screencanvas = $global.screencanvas;
function Image () {
// empty constructor
}
// NOTE: Proxy not supported on iOS 8 and 9.
// let ImageProxy = new Proxy(Image, {
// construct (target, args) {
// let img = screencanvas.createImage();
// if (!img.addEventListener) {
// img.addEventListener = function (eventName, eventCB) {
// if (eventName === 'load') {
// img.onload = eventCB;
// } else if (eventName === 'error') {
// // img.onerror = eventCB;
// }
// };
// }
// if (!img.removeEventListener) {
// img.removeEventListener = function (eventName) {
// if (eventName === 'load') {
// img.onload = null;
// } else if (eventName === 'error') {
// // img.onerror = null;
// }
// };
// }
// return img;
// },
// });
// NOTE: this is a hack operation
// let img = new window.Image()
// console.error(img instanceof window.Image) => false
// export default ImageProxy;
export default Image;

View File

@@ -0,0 +1,5 @@
export default class ImageBitmap {
constructor() {
// TODO
}
}

View File

@@ -0,0 +1,34 @@
import EventTarget from './EventTarget.js'
export default class Node extends EventTarget {
constructor() {
super()
}
childNodes = []
appendChild(node) {
this.childNodes.push(node)
// if (node instanceof Node) {
// this.childNodes.push(node)
// } else {
// throw new TypeError('Failed to executed \'appendChild\' on \'Node\': parameter 1 is not of type \'Node\'.')
// }
}
cloneNode() {
const copyNode = Object.create(this)
Object.assign(copyNode, this)
return copyNode
}
removeChild(node) {
const index = this.childNodes.findIndex((child) => child === node)
if (index > -1) {
return this.childNodes.splice(index, 1)
}
return null
}
}

View File

@@ -0,0 +1,5 @@
export default class WebGLRenderingContext {
constructor() {
// TODO
}
}

View File

@@ -0,0 +1,139 @@
const _utils = require('../../../../common/utils');
const MAX_AMOUNT_WEBSOCKET = 1 // The maximum number of WEBSOCKET
let CURR_AMOUNT_WEBSOCKET = 0 // The current number of WEBSOCKET
export default class WebSocket {
static CONNECTING = 0 // The connection is not yet open.
static OPEN = 1 // The connection is open and ready to communicate.
static CLOSING = 2 // The connection is in the process of closing.
static CLOSED = 3 // The connection is closed or couldn't be opened.
binaryType = '' // TODO 更新 binaryType
bufferedAmount = 0 // TODO 更新 bufferedAmount
extensions = ''
onopen = null
onmessage = null
onerror = null
onclose = null
_onMessage = null
_onOpen = null
_onError = null
_onClose = null
_isReduced = false
protocol = '' // TODO 小程序内目前获取不到,实际上需要根据服务器选择的 sub-protocol 返回
readyState = 3
constructor(url, protocols = []) {
if(this._isMaxCount()){
console.warn(`Failed to construct 'WebSocket': Only ${CURR_AMOUNT_WEBSOCKET} WebSocket can be created at the same time on TaoBao.`);
return this;
}
if (typeof url !== 'string' || !(/(^ws:\/\/)|(^wss:\/\/)/).test(url)) {
throw new TypeError(`Failed to construct 'WebSocket': The URL '${url}' is invalid`)
}
this.url = url
this.readyState = WebSocket.CONNECTING
this._increaseCount();
my.connectSocket({
url,
fail: function fail(res) {
this._triggerEvent('error', res)
}
})
this._onOpen = (res) => {
this.readyState = WebSocket.OPEN
this._triggerEvent('open')
}
my.onSocketOpen(this._onOpen)
this._onMessage = (res) => {
if (res && res.data && res.isBuffer) {
res.data = _utils.base64ToArrayBuffer(res.data)
}
this._triggerEvent('message', res)
}
my.onSocketMessage(this._onMessage)
this._onError = (res) => {
this._triggerEvent('error', res)
this._decreaseCount();
}
my.onSocketError(this._onError)
this._onClose = (res) => {
this.readyState = WebSocket.CLOSED
this._triggerEvent('close')
this._removeAllSocketListenr()
this._decreaseCount();
}
my.onSocketClose(this._onClose)
return this
}
close() {
this.readyState = WebSocket.CLOSING
my.closeSocket()
}
send(data) {
if (typeof data !== 'string' && !(data instanceof ArrayBuffer) && !ArrayBuffer.isView(data)) {
throw new TypeError(`Failed to send message: The data ${data} is invalid`)
}else{
var isBuffer = false;
if (data instanceof ArrayBuffer) {
data = _utils.arrayBufferToBase64(data)
isBuffer = true
}
my.sendSocketMessage({
data,
isBuffer,
fail: function (res) {
this._triggerEvent('error', res)
}
});
}
}
_triggerEvent(type, ...args) {
if (typeof this[`on${type}`] === 'function') {
this[`on${type}`].apply(this, args)
}
}
_removeAllSocketListenr(){
my.offSocketOpen(this._onOpen)
my.offSocketMessage(this._onMessage)
my.offSocketError(this._onError)
my.offSocketClose(this._onClose)
this._onOpen = null
this._onMessage = null
this._onError = null
this._onClose = null
}
_increaseCount(){
CURR_AMOUNT_WEBSOCKET += 1
}
_decreaseCount(){
if(!this._isReduced){
CURR_AMOUNT_WEBSOCKET -= 1
this._isReduced = true
}
}
_isMaxCount(){
return CURR_AMOUNT_WEBSOCKET >= MAX_AMOUNT_WEBSOCKET
}
}

View File

@@ -0,0 +1,31 @@
const { pixelRatio, windowWidth, windowHeight } = my.getSystemInfoSync()
const devicePixelRatio = pixelRatio;
let width, height;
if ($global.screencanvas.getBoundingClientRect) {
let rect = $global.screencanvas.getBoundingClientRect();
width = rect.width;
height = rect.height;
} else {
width = windowWidth;
height = windowHeight;
}
export const innerWidth = width;
export const innerHeight = height;
export { devicePixelRatio }
export const screen = {
width,
height,
availWidth: innerWidth,
availHeight: innerHeight,
availLeft: 0,
availTop: 0,
}
export const performance = {
now: Date.now
};
export const ontouchstart = null;
export const ontouchmove = null;
export const ontouchend = null;

View File

@@ -0,0 +1,172 @@
import EventTarget from './EventTarget.js'
const _url = new WeakMap()
const _method = new WeakMap()
const _requestHeader = new WeakMap()
const _responseHeader = new WeakMap()
const _requestTask = new WeakMap()
function _triggerEvent(type, ...args) {
if (typeof this[`on${type}`] === 'function') {
this[`on${type}`].apply(this, args)
}
}
function _changeReadyState(readyState) {
this.readyState = readyState
_triggerEvent.call(this, 'readystatechange')
}
export default class XMLHttpRequest extends EventTarget {
// TODO 没法模拟 HEADERS_RECEIVED 和 LOADING 两个状态
static UNSEND = 0
static OPENED = 1
static HEADERS_RECEIVED = 2
static LOADING = 3
static DONE = 4
timeout = 0;
/*
* TODO 这一批事件应该是在 XMLHttpRequestEventTarget.prototype 上面的
*/
onabort = null
onerror = null
onload = null
onloadstart = null
onprogress = null
ontimeout = null
onloadend = null
onreadystatechange = null
readyState = 0
response = null
responseText = null
responseType = ''
responseXML = null
status = 0
statusText = ''
upload = {}
withCredentials = false
constructor() {
super();
_requestHeader.set(this, {
'content-type': 'application/json'
})
_responseHeader.set(this, {})
}
abort() {
const myRequestTask = _requestTask.get(this)
if (myRequestTask) {
myRequestTask.abort()
}
}
getAllResponseHeaders() {
const responseHeader = _responseHeader.get(this)
return Object.keys(responseHeader).map((header) => {
return `${header}: ${responseHeader[header]}`
}).join('\n')
}
getResponseHeader(header) {
return _responseHeader.get(this)[header]
}
open(method, url/* async, user, password 这几个参数在小程序内不支持*/) {
_method.set(this, method)
_url.set(this, url)
_changeReadyState.call(this, XMLHttpRequest.OPENED)
}
overrideMimeType() {
}
send(data = '') {
if (this.readyState !== XMLHttpRequest.OPENED) {
throw new Error("Failed to execute 'send' on 'XMLHttpRequest': The object's state must be OPENED.")
} else {
let myRequestTask = my.request({
data,
url: _url.get(this),
method: _method.get(this),
headers: _requestHeader.get(this),
dataType: 'other',
responseType: this.responseType === 'arraybuffer' ? 'arraybuffer' : 'text',
timeout: this.timeout || undefined,
success: ({ data, status, headers }) => {
this.status = status
_responseHeader.set(this, headers)
_triggerEvent.call(this, 'loadstart')
_changeReadyState.call(this, XMLHttpRequest.HEADERS_RECEIVED)
_changeReadyState.call(this, XMLHttpRequest.LOADING)
switch (this.responseType) {
case 'json':
this.responseText = data;
try {
this.response = JSON.parse(data);
}
catch (e) {
this.response = null;
}
break;
case '':
case 'text':
this.responseText = this.response = data;
break;
case 'arraybuffer':
this.response = data;
this.responseText = '';
var bytes = new Uint8Array(data);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
this.responseText += String.fromCharCode(bytes[i]);
}
break;
default:
this.response = null;
}
_changeReadyState.call(this, XMLHttpRequest.DONE)
_triggerEvent.call(this, 'load')
_triggerEvent.call(this, 'loadend')
},
fail: ({ error, errorMessage }) => {
// TODO 规范错误
if (9 === error || errorMessage.indexOf('abort') !== -1) {
_triggerEvent.call(this, 'abort')
} else if (13 === error || errorMessage.indexOf('超时') !== -1) {
_triggerEvent.call(this, 'timeout')
} else {
_triggerEvent.call(this, 'error', errorMessage)
}
_triggerEvent.call(this, 'loadend')
}
})
_requestTask.set(this, myRequestTask);
}
}
setRequestHeader(header, value) {
const myHeader = _requestHeader.get(this)
myHeader[header] = value
_requestHeader.set(this, myHeader)
}
addEventListener(type, listener) {
if (typeof listener === 'function') {
let _this = this
let event = { target: _this }
this['on' + type] = function (event) {
listener.call(_this, event)
}
}
}
}

View File

@@ -0,0 +1,4 @@
let screencanvas = $global.screencanvas;
let cancelAnimationFrame = screencanvas.cancelAnimationFrame.bind(screencanvas);
export default cancelAnimationFrame;

View File

@@ -0,0 +1,124 @@
import * as _window from './window'
import HTMLElement from './HTMLElement'
import Image from './Image'
import Canvas from './Canvas'
import Audio from './Audio'
const events = {}
var document = {
readyState: 'complete',
visibilityState: 'visible',
documentElement: _window,
hidden: false,
style: {},
location: _window.location,
ontouchstart: null,
ontouchmove: null,
ontouchend: null,
head: new HTMLElement('head'),
body: new HTMLElement('body'),
createElement(tagName) {
tagName = tagName.toLowerCase();
if (tagName === 'canvas') {
return new Canvas()
} else if (tagName === 'audio') {
return new Audio()
} else if (tagName === 'img') {
return new Image()
}
return new HTMLElement(tagName)
},
createElementNS(nameSpace, tagName) {
return this.createElement(tagName);
},
getElementById(id) {
if (id === _window.canvas.id) {
return _window.canvas
}
return null
},
getElementsByTagName(tagName) {
if (tagName === 'head') {
return [document.head]
} else if (tagName === 'body') {
return [document.body]
} else if (tagName === 'canvas') {
return [_window.canvas]
}
return []
},
getElementsByName(tagName) {
if (tagName === 'head') {
return [document.head]
} else if (tagName === 'body') {
return [document.body]
} else if (tagName === 'canvas') {
return [_window.canvas]
}
return []
},
querySelector(query) {
if (query === 'head') {
return document.head
} else if (query === 'body') {
return document.body
} else if (query === 'canvas') {
return _window.canvas
} else if (query === `#${_window.canvas.id}`) {
return _window.canvas
}
return null
},
querySelectorAll(query) {
if (query === 'head') {
return [document.head]
} else if (query === 'body') {
return [document.body]
} else if (query === 'canvas') {
return [_window.canvas]
}
return []
},
addEventListener(type, listener) {
if (!events[type]) {
events[type] = []
}
events[type].push(listener)
},
removeEventListener(type, listener) {
const listeners = events[type]
if (listeners && listeners.length > 0) {
for (let i = listeners.length; i--; i > 0) {
if (listeners[i] === listener) {
listeners.splice(i, 1)
break
}
}
}
},
dispatchEvent(event) {
const listeners = events[event.type]
if (listeners) {
for (let i = 0; i < listeners.length; i++) {
listeners[i](event)
}
}
}
}
export default document

View File

@@ -0,0 +1,61 @@
import * as _window from './window'
import * as _document from './document'
var global = $global;
function inject () {
// 暴露全局的 canvas
_window.canvas = $global.screencanvas;
_window.document = _document;
_window.addEventListener = (type, listener) => {
_window.document.addEventListener(type, listener)
}
_window.removeEventListener = (type, listener) => {
_window.document.removeEventListener(type, listener)
}
_window.dispatchEvent = _window.document.dispatchEvent;
const { platform } = my.getSystemInfoSync()
// 开发者工具无法重定义 window
if (typeof __devtoolssubcontext === 'undefined' && platform === 'devtools') {
for (const key in _window) {
const descriptor = Object.getOwnPropertyDescriptor(global, key)
if (!descriptor || descriptor.configurable === true) {
Object.defineProperty(window, key, {
value: _window[key]
})
}
}
for (const key in _window.document) {
const descriptor = Object.getOwnPropertyDescriptor(global.document, key)
if (!descriptor || descriptor.configurable === true) {
Object.defineProperty(global.document, key, {
value: _window.document[key]
})
}
}
window.parent = window
} else {
for (const key in _window) {
global[key] = _window[key]
}
// global.window = _window
// window = global
// window.top = window.parent = window
}
global.setTimeout = setTimeout;
global.clearTimeout = clearTimeout;
global.setInterval = setInterval;
global.clearInterval = clearInterval;
}
if (!global.__isAdapterInjected) {
global.__isAdapterInjected = true
inject()
}

View File

@@ -0,0 +1,37 @@
const localStorage = {
get length() {
const { keys } = my.getStorageInfoSync()
return keys.length
},
key(n) {
const { keys } = my.getStorageInfoSync()
return keys[n]
},
getItem(key) {
let ret = my.getStorageSync({
key,
});
return ret && ret.data;
},
setItem(key, data) {
return my.setStorageSync({
key,
data,
});
},
removeItem(key) {
my.removeStorageSync(key)
},
clear() {
my.clearStorageSync()
}
}
export default localStorage

View File

@@ -0,0 +1,7 @@
var location = {
href: 'game.js',
reload() {
}
}
export default location

View File

@@ -0,0 +1,38 @@
import { noop } from './util/index.js'
// TODO 需要 my.getSystemInfo 获取更详细信息
const systemInfo = my.getSystemInfoSync()
console.log(systemInfo)
const system = systemInfo.system;
const platform = systemInfo.platform;
const language = systemInfo.language;
const version = systemInfo.version;
const android = system ? system.toLowerCase().indexOf('android') !== -1 : false;
const uaDesc = android ? `Android; CPU ${system}` : `iPhone; CPU iPhone OS ${system} like Mac OS X`;
const ua = `Mozilla/5.0 (${uaDesc}) AppleWebKit/603.1.30 (KHTML, like Gecko) Mobile/14E8301 MicroMessenger/${version} MiniGame NetType/WIFI Language/${language}`;
const navigator = {
platform,
language: language,
appVersion: `5.0 (${uaDesc}) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1`,
userAgent: ua,
onLine: true, // TODO 用 my.getNetworkStateChange 和 my.onNetworkStateChange 来返回真实的状态
// TODO 用 my.getLocation 来封装 geolocation
geolocation: {
getCurrentPosition: noop,
watchPosition: noop,
clearWatch: noop
}
}
if (my.onNetworkStatusChange) {
my.onNetworkStatusChange(function(event){
navigator.onLine = event.isConnected;
});
}
export default navigator

View File

@@ -0,0 +1,4 @@
let screencanvas = $global.screencanvas;
let requestAnimationFrame = screencanvas.requestAnimationFrame.bind(screencanvas);
export default requestAnimationFrame;

View File

@@ -0,0 +1 @@
export function noop() {}

View File

@@ -0,0 +1,15 @@
export navigator from './navigator'
export XMLHttpRequest from './XMLHttpRequest'
export WebSocket from './WebSocket'
export Image from './Image'
export ImageBitmap from './ImageBitmap'
export HTMLElement from './HTMLElement'
export HTMLImageElement from './HTMLImageElement'
export HTMLCanvasElement from './HTMLCanvasElement'
export WebGLRenderingContext from './WebGLRenderingContext'
export localStorage from './localStorage'
export location from './location'
export requestAnimationFrame from './requestAnimationFrame'
export cancelAnimationFrame from './cancelAnimationFrame'
export * from './WindowProperties'

View File

@@ -0,0 +1,108 @@
const screencanvas = $global.screencanvas;
const parser = cc.assetManager.parser;
const downloader = cc.assetManager.downloader;
function parseParameters (options, onProgress, onComplete) {
if (onComplete === undefined) {
var isCallback = typeof options === 'function';
if (onProgress) {
onComplete = onProgress;
if (!isCallback) {
onProgress = null;
}
}
else if (onProgress === undefined && isCallback) {
onComplete = options;
options = null;
onProgress = null;
}
if (onProgress !== undefined && isCallback) {
onProgress = options;
options = null;
}
}
options = options || Object.create(null);
return { options, onProgress, onComplete };
}
function doNothing (url, options, onComplete) {
onComplete(null, url);
}
downloader.downloadDomAudio = doNothing;
function downloadImage (url, options, onComplete) {
const parameters = parseParameters(options, undefined, onComplete);
options = parameters.options;
onComplete = parameters.onComplete;
const img = screencanvas.createImage();
const timer = setTimeout(() => {
clearEvent();
onComplete && onComplete(new Error(cc.debug.getError(4930, url)));
}, 8000);
function clearEvent () {
img.onload = null;
// img.onerror = null;
}
function loadCallback () {
clearEvent();
clearTimeout(timer);
onComplete && onComplete(null, img);
}
function errorCallback () {
clearEvent();
clearTimeout(timer);
onComplete && onComplete(new Error(cc.debug.getError(4930, url)));
}
img.onload = loadCallback;
// NOTE: crash when registering error callback
// img.onerror = errorCallback;
img.src = url;
return img;
}
downloader.downloadDomImage = downloadImage;
downloader.register({
// Audio
'.mp3' : doNothing,
'.ogg' : doNothing,
'.wav' : doNothing,
'.m4a' : doNothing,
// Image
'.png' : doNothing,
'.jpg' : doNothing,
'.bmp' : doNothing,
'.jpeg' : doNothing,
'.gif' : doNothing,
'.ico' : doNothing,
'.tiff' : doNothing,
'.image' : doNothing,
'.webp' : doNothing,
'.pvr': doNothing,
'.pkm': doNothing,
'.astc': doNothing,
});
parser.register({
// Audio
'.mp3' : doNothing,
'.ogg' : doNothing,
'.wav' : doNothing,
'.m4a' : doNothing,
// Image
'.png' : downloadImage,
'.jpg' : downloadImage,
'.bmp' : downloadImage,
'.jpeg' : downloadImage,
'.gif' : downloadImage,
'.ico' : downloadImage,
'.tiff' : downloadImage,
'.image' : downloadImage,
'.webp' : downloadImage,
});

View File

@@ -0,0 +1,171 @@
const game = cc.game;
const EventTarget = cc.EventTarget;
const State = {
ERROR: -1,
INITIALZING: 0,
PLAYING: 1,
PAUSED: 2,
STOPPED: 3,
}
function Audio (url, serializedDuration) {
this._nativeAudio = my.createInnerAudioContext();
this._et = new EventTarget();
this.reset();
this._setSrc(url);
const nativeAudio = this._nativeAudio;
this._serializedDuration = serializedDuration;
this._ensureLoaded(() => {
this._duration = nativeAudio.duration;
});
this._duration = 1;
this._onShow = () => {
if (this._blocked) {
this._nativeAudio.play();
}
this._blocked = false;
};
this._onHide = () => {
if (this.getState() === State.PLAYING) {
this._nativeAudio.pause();
this._blocked = true;
}
};
nativeAudio.onCanplay(() => { this._et.emit('load'); });
nativeAudio.onError((err) => { this._et.emit('error', err); });
nativeAudio.onEnded(() => {
this.finishCB && this.finishCB();
this._state = State.INITIALZING;
this._et.emit('ended');
});
nativeAudio.onStop(() => { this._et.emit('stop'); });
nativeAudio.onTimeUpdate(() => { this._currentTime = nativeAudio.currentTime; });
game.on(game.EVENT_SHOW, this._onShow);
game.on(game.EVENT_HIDE, this._onHide);
this.onError((err) => { cc.error(err); });
}
Audio.State = State;
Object.assign(Audio.prototype, {
reset () {
this.id = -1;
this.finishCB = null; // For audioEngine custom ended callback.
this._state = State.INITIALZING;
this._loop = false;
this._currentTime = 0;
this._volume = 1;
this._blocked = false;
this._loaded = false;
this.offLoad();
this.offError();
this.offEnded();
this.offStop();
},
destroy () {
this.reset();
game.off(game.EVENT_SHOW, this._onShow);
game.off(game.EVENT_HIDE, this._onHide);
// offCanplay offOnError offStop offEnded is not supported for now.
this._nativeAudio.destroy();
this._nativeAudio = null;
},
getSrc () { return this._src; },
// NOTE: don't set src twice, which is not supported on TAOBAO
_setSrc (path) {
if (this._src === path) {
return;
}
const nativeAudio = this._nativeAudio;
this._loaded = false;
nativeAudio.src = path;
this._src = path;
},
getState () { return this._state; },
getDuration () { return this._serializedDuration ? this._serializedDuration : this._duration; },
getCurrentTime () { return this._currentTime; },
seek (val) {
if (this._currentTime === val) {
return;
}
this._ensureLoaded(() => {
this._nativeAudio.seek(val);
this._currentTime = val;
});
},
getLoop () { return this._loop; },
setLoop (val) {
if (this._loop === val) {
return;
}
this._ensureLoaded(() => {
this._nativeAudio.loop = val;
this._loop = val;
});
},
getVolume () { return this._volume; },
setVolume (val) {
if (this._volume === val) {
return;
}
this._ensureLoaded(() => {
this._nativeAudio.volume = val;
this._volume = val;
});
},
play () {
if (this.getState() !== State.PLAYING) {
this._nativeAudio.play();
this._state = State.PLAYING;
}
},
resume () {
if (this.getState() === State.PAUSED) {
this._nativeAudio.play();
this._state = State.PLAYING;
}
},
pause () {
if (this.getState() === State.PLAYING) {
this._nativeAudio.pause();
this._state = State.PAUSED;
}
},
stop () {
// NOTE: On taobao, it is designed that audio is useless after stopping.
// this._nativeAudio.stop();
this._nativeAudio.pause();
this._nativeAudio.seek(0);
this._state = State.STOPPED;
},
onceLoad (cb) { this._et.once('load', cb); },
onLoad (cb) { this._et.on('load', cb); },
offLoad (cb = undefined) { this._et.off('load', cb); },
onError (cb) { this._et.on('error', cb); },
offError (cb = undefined) { this._et.off('error', cb); },
onEnded (cb) { this._et.on('ended', cb); },
offEnded (cb = undefined) { this._et.off('ended', cb); },
onStop (cb) { this._et.on('stop', cb); },
offStop (cb = undefined) { this._et.off('stop', cb); },
_ensureLoaded (cb) {
if (this._loaded) {
cb();
} else {
this.onceLoad(() => {
this._loaded = true;
cb();
});
}
}
});
module.exports = Audio;

View File

@@ -0,0 +1,389 @@
const Audio = require('./Audio');
let _instanceId = 0;
const _id2audio = {};
const _audioPool = [];
const _maxPoolSize = 32;
function handleVolume (volume) {
if (volume === undefined) {
// set default volume as 1
volume = 1;
}
else if (typeof volume === 'string') {
volume = Number.parseFloat(volume);
}
return volume;
};
function getOrCreateAudio (path, serializedDuration) {
let audio;
_audioPool.some((item, index) => {
if (item.getSrc() === path) {
audio = item;
_audioPool.splice(index, 1);
return true;
}
return false;
});
if (!audio) {
audio = new Audio(path, serializedDuration);
}
audio.id = ++_instanceId;
audio.onEnded(() => {
putOrDestroyAudio(audio);
});
return audio;
}
function putOrDestroyAudio (audio) {
if (_audioPool.includes(audio)) {
return;
}
delete _id2audio[audio.id];
audio.reset();
if (_audioPool.length < _maxPoolSize) {
_audioPool.push(audio);
} else {
audio.destroy();
}
}
const _maxPlayingAudio = 10;
cc.audioEngine = {
AudioState: Audio.State,
_maxPoolSize: 32,
_id2audio,
_pauseIDCache: [],
_play (clip, loop, volume) {
let path;
if (typeof clip === 'string') {
path = clip;
} else {
path = clip.nativeUrl;
}
let audio = getOrCreateAudio(path, clip.duration);
volume = handleVolume(volume);
audio.setLoop(loop || false);
audio.setVolume(volume);
audio.play();
return audio;
},
play: function (clip, loop, volume) {
const audio = this._play(clip, loop, volume);
this._id2audio[audio.id] = audio;
return audio.id;
},
setLoop: function (id, loop) {
const audio = this._id2audio[id];
if (audio) {
audio.setLoop(loop);
}
},
isLoop: function (id) {
const audio = this._id2audio[id];
if (audio) {
return audio.getLoop();
}
return false;
},
setVolume: function (id, volume) {
volume = handleVolume(volume);
const audio = this._id2audio[id];
if (audio) {
return audio.setVolume(volume);
}
},
getVolume: function (id) {
const audio = this._id2audio[id];
if (audio) {
return audio.getVolume();
}
return 1;
},
setCurrentTime: function (id, sec) {
const audio = this._id2audio[id];
if (audio) {
return audio.seek(sec);
}
},
getCurrentTime: function (id) {
const audio = this._id2audio[id];
if (audio) {
return audio.getCurrentTime();
}
return 0;
},
getDuration: function (id) {
const audio = this._id2audio[id];
if (audio) {
return audio.getDuration();
}
return 1;
},
getState: function (id) {
const audio = this._id2audio[id];
if (audio) {
return audio.getState();
}
return Audio.State.INITIALZING;
},
isPlaying: function(id) {
const audio = this._id2audio[id];
if (audio) {
return audio.getState() === Audio.State.PLAYING;
}
return false;
},
setFinishCallback: function (id, callback) {
const audio = this._id2audio[id];
if (audio) {
return audio.finishCB = callback;
}
},
pause: function (id) {
const audio = this._id2audio[id];
if (audio) {
audio.pause();
}
},
pauseAll: function () {
for (let id in this._id2audio) {
const audio = this._id2audio[id];
if (audio) {
audio.pause();
}
}
},
resume: function (id) {
const audio = this._id2audio[id];
if (audio) {
audio.resume();
}
},
resumeAll: function () {
for (let id in this._id2audio) {
const audio = this._id2audio[id];
if (audio) {
audio.resume();
}
}
},
stop: function (id) {
const audio = this._id2audio[id];
if (audio) {
audio.stop();
}
},
stopAll: function () {
for (let id in this._id2audio) {
const audio = this._id2audio[id];
if (audio) {
audio.stop();
}
}
},
setMaxAudioInstance: function (num) {
// NOT SUPPPORTED
},
getMaxAudioInstance: function () {
return _maxPlayingAudio;
},
uncache: function (clip) {
var filePath = clip;
if (typeof clip === 'string') {
// backward compatibility since 1.10
cc.warnID(8401, 'cc.audioEngine', 'cc.AudioClip', 'AudioClip', 'cc.AudioClip', 'audio');
filePath = clip;
}
else {
if (!clip) {
return;
}
filePath = clip.nativeUrl;
}
for (let id in _id2audio) {
const audio = this._id2audio[id];
if (audio && audio.getSrc() === filePath) {
audio.stop();
putOrDestroyAudio(audio);
}
}
},
uncacheAll: function () {
this.stopAll();
for (let id in _id2audio) {
const audio = _id2audio[id];
if (audio) {
audio.stop();
putOrDestroyAudio(audio);
}
}
},
_break: function () {
// DO NOTHING
},
_restore: function () {
// DO NOTHING
},
///////////////////////////////
// Classification of interface
_music: null,
_effectVolume: 1,
playMusic: function (clip, loop) {
if (this._music) {
if (this._music.getSrc() !== clip.nativeUrl) {
this._music.stop();
putOrDestroyAudio(this._music);
const audio = this._play(clip, loop);
this._music = audio;
} else {
this._music.stop();
this._music.play();
}
} else {
const audio = this._play(clip, loop);
this._music = audio;
}
return this._music.id;
},
stopMusic: function () {
this._music.stop();
},
pauseMusic: function () {
this._music.pause();
},
resumeMusic: function () {
this._music.resume();
},
getMusicVolume: function () {
if (this._music) {
return this._music.getVolume();
}
return 1;
},
setMusicVolume: function (volume) {
volume = handleVolume(volume);
if (this._music) {
this._music.setVolume(volume);
}
},
isMusicPlaying: function () {
return this._music.getState() === Audio.State.PLAYING;
},
playEffect: function (clip, loop) {
return this.play(clip, loop, this._effectVolume);
},
setEffectsVolume: function (volume) {
volume = handleVolume(volume);
var musicId = this._music.id;
this._effectVolume = volume;
for (var id in _id2audio) {
var audio = _id2audio[id];
if (!audio || audio.id === musicId) continue;
audio.setVolume(volume);
}
},
getEffectsVolume: function () {
return this._effectVolume;
},
pauseEffect: function (id) {
return this.pause(id);
},
pauseAllEffects: function () {
var musicId = this._music.id;
for (var id in _id2audio) {
var audio = _id2audio[id];
if (!audio || audio.id === musicId) continue;
audio.pause();
}
},
resumeEffect: function (id) {
this.resume(id);
},
resumeAllEffects: function () {
var musicId = this._music.id;
for (var id in _id2audio) {
var audio = _id2audio[id];
if (!audio || audio.id === musicId) continue;
audio.resume();
}
},
stopEffect: function (id) {
return this.stop(id);
},
stopAllEffects: function () {
var musicId = this._music.id;
for (var id in _id2audio) {
var audio = _id2audio[id];
if (!audio || audio.id === musicId) continue;
audio.stop();
}
}
};

View File

@@ -0,0 +1,139 @@
(function(){
if (!(cc && cc.AudioClip && cc.AudioSource)) {
return;
}
const Audio = require('./Audio');
const AudioClip = cc.AudioClip;
const proto = cc.AudioSource.prototype;
Object.defineProperties(proto, {
isPlaying: {
get () {
var state = this.audio.getState();
return state === cc._Audio.State.PLAYING;
}
},
clip: {
get: function () {
return this._clip;
},
set: function (value) {
if (value === this._clip) {
return;
}
if (!(value instanceof AudioClip)) {
return cc.error('Wrong type of AudioClip.');
}
this._clip = value;
this.audio.stop();
this.audio.destroy();
this.audio = new Audio(value.nativeUrl, value.duration);
},
},
volume: {
get: function () {
return this._volume;
},
set: function (value) {
value = misc.clamp01(value);
this._volume = value;
if (!this._mute) {
this.audio.setVolume(value);
}
return value;
},
},
mute: {
get: function () {
return this._mute;
},
set: function (value) {
if (this._mute === value) {
return;
}
this._mute = value;
this.audio.setVolume(value ? 0 : this._volume);
return value;
},
},
loop: {
get: function () {
return this._loop;
},
set: function (value) {
this._loop = value;
this.audio.setLoop(value);
return value;
}
},
})
Object.assign(proto, {
onLoad: function () {
if(this.audio && this.audio instanceof Audio)
return;
if (this._clip) {
this.audio = new Audio(this._clip.nativeUrl, this._clip.duration);
}
},
onEnable: function () {
if (this.playOnLoad && this._firstlyEnabled) {
this._firstlyEnabled = false;
this.play();
}
},
onDisable: function () {
this.stop();
},
onDestroy: function () {
this.audio.destroy();
},
play: function () {
if ( !this._clip ) return;
var audio = this.audio;
audio.setVolume(this._mute ? 0 : this._volume);
audio.setLoop(this._loop);
audio.seek(0);
audio.play();
},
stop: function () {
this.audio.stop();
},
pause: function () {
this.audio.pause();
},
resume: function () {
this.audio.resume();
},
rewind: function(){
this.audio.seek(0);
},
getCurrentTime: function () {
return this.audio.getCurrentTime();
},
setCurrentTime: function (time) {
this.audio.seek(time);
return time;
},
getDuration: function () {
return this.audio.getDuration();
}
});
})();

View File

@@ -0,0 +1,92 @@
(function () {
if (!(cc && cc.EditBox)) {
return;
}
const EditBox = cc.EditBox;
const js = cc.js;
const KeyboardReturnType = EditBox.KeyboardReturnType;
const MAX_VALUE = 65535;
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);
}
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;
}
let delegate = this._delegate;
this._showPrompt();
this._editing = true;
delegate.editBoxEditingDidBegan();
},
endEditing () {
cc.warn(`Can't support to end editing.`);
},
_showPrompt () {
let self = this;
let delegate = this._delegate;
let multiline = (delegate.inputMode === EditBox.InputMode.ANY);
let maxLength = (delegate.maxLength < 0 ? MAX_VALUE : delegate.maxLength);
if (multiline) {
cc.warn(`Multiline editing is not supported`);
}
my.prompt({
title: '',
message: delegate.placeholder,
// placeholder: delegate.placeholder,
okButtonText: getKeyboardReturnType(delegate.returnType),
cancelButtonText: 'cancel',
success: (result) => {
if (result.ok) {
let inputValue = result.inputValue;
inputValue = (maxLength <= inputValue.length) ? inputValue.substring(0, maxLength) : inputValue;
if (delegate._string !== inputValue) {
delegate.editBoxTextChanged(inputValue);
}
delegate.editBoxEditingReturn();
}
self._editing = false;
delegate.editBoxEditingDidEnded();
},
});
},
});
})();

View File

@@ -0,0 +1,55 @@
if (cc && cc.Label) {
const gfx = cc.gfx;
const Label = cc.Label;
// shared label canvas
let _sharedLabelCanvas = my.createOffscreenCanvas();
let _sharedLabelCanvasCtx = _sharedLabelCanvas.getContext('2d');
let canvasData = {
canvas: _sharedLabelCanvas,
context: _sharedLabelCanvasCtx,
};
cc.game.on(cc.game.EVENT_ENGINE_INITED, function () {
Object.assign(Label._canvasPool, {
get() {
return canvasData;
},
put() {
// do nothing
}
});
});
let _originUpdateMaterial = Label.prototype._updateMaterialWebgl;
// fix ttf font black border
Object.assign(Label.prototype, {
_updateMaterialWebgl () {
_originUpdateMaterial.call(this);
// init blend factor
let material = this._materials[0];
if (!this._frame || !material) {
return;
}
let dstBlendFactor = cc.macro.BlendFactor.ONE_MINUS_SRC_ALPHA;
let srcBlendFactor;
if (!(__globalAdapter.isDevTool || this.font instanceof cc.BitmapFont)) {
// Premultiplied alpha on runtime
srcBlendFactor = cc.macro.BlendFactor.ONE;
}
else {
srcBlendFactor = cc.macro.BlendFactor.SRC_ALPHA;
}
// set blend func
material.effect.setBlend(
true,
gfx.BLEND_FUNC_ADD,
srcBlendFactor, dstBlendFactor,
gfx.BLEND_FUNC_ADD,
srcBlendFactor, dstBlendFactor,
);
},
});
}

View File

@@ -0,0 +1,2 @@
// NOTE: can't cache file on Taobao iOS end
cc.assetManager.cacheManager.cacheEnabled = cc.sys.os !== cc.sys.OS_IOS;

View File

@@ -0,0 +1,6 @@
require('./AudioEngine');
require('./AudioSource');
require('./AssetManager');
require('./cache-manager');
require('./Label');
require('./Editbox');

View File

@@ -0,0 +1,262 @@
/****************************************************************************
Copyright (c) 2017-2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this 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 this 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.
****************************************************************************/
// TODO: verify the my API
var fs = my.getFileSystemManager ? my.getFileSystemManager() : null;
var outOfStorageRegExp = /the maximum size of the file storage/; // not exactly right
var fsUtils = {
fs,
isOutOfStorage (errMsg) {
return outOfStorageRegExp.test(errMsg);
},
getUserDataPath () {
return my.env.USER_DATA_PATH;
},
checkFsValid () {
if (!fs) {
console.warn('Can not get the file system!');
return false;
}
return true;
},
deleteFile (filePath, onComplete) {
fs.unlink({
filePath: filePath,
success: function () {
onComplete && onComplete(null);
},
fail: function (res) {
console.warn(`Delete file failed: path: ${filePath} message: ${res.errorMessage}`);
onComplete && onComplete(new Error(res.errorMessage));
}
});
},
downloadFile (remoteUrl, filePath, header, onProgress, onComplete) {
var options = {
url: remoteUrl,
success: function (res) {
if (!filePath) {
onComplete && onComplete(null, res.apFilePath);
}
else {
fsUtils.copyFile(res.apFilePath, filePath, onComplete);
}
},
fail: function (res) {
console.warn(`Download file failed: path: ${remoteUrl} message: ${res.errorMessage}`);
onComplete && onComplete(new Error(res.errorMessage), null);
}
}
if (header) options.header = header;
var task = my.downloadFile(options);
onProgress && task.onProgressUpdate(onProgress);
},
saveFile (srcPath, destPath, onComplete) {
// Hack, seems like my.saveFile dose not work
fsUtils.copyFile(srcPath, destPath, onComplete);
},
copyFile (srcPath, destPath, onComplete) {
fs.copyFile({
srcPath: srcPath,
destPath: destPath,
success: function () {
onComplete && onComplete(null);
},
fail: function (res) {
console.warn(`Copy file failed: path: ${srcPath} message: ${res.errorMessage}`);
onComplete && onComplete(new Error(res.errorMessage));
}
});
},
writeFile (path, data, encoding, onComplete) {
fs.writeFile({
filePath: path,
encoding: encoding,
data: data,
success: function () {
onComplete && onComplete(null);
},
fail: function (res) {
console.warn(`Write file failed: path: ${path} message: ${res.errorMessage}`);
onComplete && onComplete(new Error(res.errorMessage));
}
});
},
writeFileSync (path, data, encoding) {
try {
fs.writeFileSync({
filePath: path,
data: data,
encoding: encoding,
});
return null;
}
catch (e) {
console.warn(`Write file failed: path: ${path} message: ${e.message}`);
return new Error(e.message);
}
},
readFile (filePath, encoding, onComplete) {
fs.readFile({
filePath: filePath,
encoding: encoding,
success: function (res) {
onComplete && onComplete(null, res.data);
},
fail: function (res) {
console.warn(`Read file failed: path: ${filePath} message: ${res.errorMessage}`);
onComplete && onComplete (new Error(res.errorMessage), null);
}
});
},
readDir (filePath, onComplete) {
fs.readdir({
dirPath: filePath,
success: function (res) {
onComplete && onComplete(null, res.files);
},
fail: function (res) {
console.warn(`Read directory failed: path: ${filePath} message: ${res.errorMessage}`);
onComplete && onComplete(new Error(res.errorMessage), null);
}
});
},
readText (filePath, onComplete) {
fsUtils.readFile(filePath, 'utf8', onComplete);
},
readArrayBuffer (filePath, onComplete) {
fsUtils.readFile(filePath, '', onComplete);
},
readJson (filePath, onComplete) {
fsUtils.readFile(filePath, 'utf8', function (err, text) {
var out = null;
if (!err) {
try {
out = JSON.parse(text);
}
catch (e) {
console.warn(`Read json failed: path: ${filePath} message: ${e.message}`);
err = new Error(e.message);
}
}
onComplete && onComplete(err, out);
});
},
readJsonSync (path) {
try {
var res = fs.readFileSync({
filePath: path,
encoding: 'utf8',
});
return JSON.parse(res.data);
}
catch (e) {
console.warn(`Read json failed: path: ${path} message: ${e.message}`);
return new Error(e.message);
}
},
makeDirSync (path, recursive) {
try {
fs.mkdirSync({
dirPath: path,
recursive: recursive,
});
return null;
}
catch (e) {
console.warn(`Make directory failed: path: ${path} message: ${e.message}`);
return new Error(e.message);
}
},
rmdirSync (dirPath, recursive) {
try {
fs.rmdirSync({ dirPath, recursive });
}
catch (e) {
console.warn(`rm directory failed: path: ${dirPath} message: ${e.message}`);
return new Error(e.message);
}
},
exists (filePath, onComplete) {
// fs.access is not supported.
// fs.access({
// path: filePath,
// success: function () {
// onComplete && onComplete(true);
// },
// fail: function () {
// onComplete && onComplete(false);
// }
// });
fs.readFile({
filePath: filePath,
success () {
onComplete && onComplete(true);
},
fail () {
onComplete && onComplete (false);
}
});
},
loadSubpackage (name, onProgress, onComplete) {
throw new Error('Not Implemented');
},
unzip (zipFilePath, targetPath, onComplete) {
fs.unzip({
zipFilePath,
targetPath,
success () {
onComplete && onComplete(null);
},
fail (res) {
console.warn(`unzip failed: path: ${zipFilePath} message: ${res.errorMessage}`);
onComplete && onComplete(new Error('unzip failed: ' + res.errorMessage));
},
})
},
};
window.fsUtils = module.exports = fsUtils;

View File

@@ -0,0 +1,19 @@
const adapter = window.__globalAdapter;
let adaptSysFunc = adapter.adaptSys;
//taobao IDE language ("Chinese")
//taobao phone language (Andrond: "cn", iPad: 'zh_CN')
const languageMap = {
Ch: "zh",
cn: "zh",
zh: "zh",
};
Object.assign(adapter, {
// Extend adaptSys interface
adaptSys (sys) {
adaptSysFunc.call(this, sys);
sys.platform = sys.TAOBAO;
sys.language = languageMap[sys.language] || sys.language;
},
});

View File

@@ -0,0 +1,106 @@
const utils = require('../../../common/utils');
if (window.__globalAdapter) {
let globalAdapter = window.__globalAdapter;
// SystemInfo
globalAdapter.isSubContext = false; // sub context not supported
globalAdapter.isDevTool = my.isIDE;
utils.cloneMethod(globalAdapter, my, 'getSystemInfoSync');
// Audio
globalAdapter.createInnerAudioContext = function () {
let audio = my.createInnerAudioContext();
if (my.getSystemInfoSync().platform === 'iOS') {
let currentTime = 0;
let originalSeek = audio.seek;
audio.seek = function (time) {
// need to access audio.paused in the next tick
setTimeout(() => {
if (audio.paused) {
currentTime = time;
} else {
originalSeek.call(audio, time);
}
}, 50);
};
let originalPlay = audio.play;
audio.play = function () {
if (currentTime !== 0) {
audio.seek(currentTime);
currentTime = 0; // clear cached currentTime
}
originalPlay.call(audio);
};
}
return audio;
};
// FrameRate
// utils.cloneMethod(globalAdapter, my, 'setPreferredFramesPerSecond');
// Keyboard
globalAdapter.showKeyboard = () => console.warn('showKeyboard not supported.');
globalAdapter.hideKeyboard = () => console.warn('hideKeyboard not supported.');
globalAdapter.updateKeyboard = () => console.warn('updateKeyboard not supported.');
globalAdapter.onKeyboardInput = () => console.warn('onKeyboardInput not supported.');
globalAdapter.onKeyboardConfirm = () => console.warn('onKeyboardConfirm not supported.');
globalAdapter.onKeyboardComplete = () => console.warn('onKeyboardComplete not supported.');
globalAdapter.offKeyboardInput = () => console.warn('offKeyboardInput not supported.');
globalAdapter.offKeyboardConfirm = () => console.warn('offKeyboardConfirm not supported.');
globalAdapter.offKeyboardComplete = () => console.warn('offKeyboardComplete not supported.');
// Message
utils.cloneMethod(globalAdapter, my, 'getOpenDataContext');
utils.cloneMethod(globalAdapter, my, 'onMessage');
// Subpackage not supported
// utils.cloneMethod(globalAdapter, my, 'loadSubpackage');
// SharedCanvas
utils.cloneMethod(globalAdapter, my, 'getSharedCanvas');
// Font
globalAdapter.loadFont = function (url) {
// my.loadFont crash when url is not in user data path
return "Arial";
};
// hide show Event
utils.cloneMethod(globalAdapter, my, 'onShow');
utils.cloneMethod(globalAdapter, my, 'onHide');
// Accelerometer
let accelerometerCallback = null;
let systemInfo = my.getSystemInfoSync();
let windowWidth = systemInfo.windowWidth;
let windowHeight = systemInfo.windowHeight;
let isLandscape = windowWidth > windowHeight;
function accelerometerChangeCallback (res, cb) {
let resClone = {};
let x = res.x;
let y = res.y;
if (isLandscape) {
let tmp = x;
x = -y;
y = tmp;
}
resClone.x = x;
resClone.y = y;
resClone.z = res.z;
accelerometerCallback && accelerometerCallback(resClone);
}
Object.assign(globalAdapter, {
startAccelerometer (cb) {
accelerometerCallback = cb;
my.onAccelerometerChange && my.onAccelerometerChange(accelerometerChangeCallback);
},
stopAccelerometer () {
my.offAccelerometerChange && my.offAccelerometerChange(accelerometerChangeCallback);
},
});
}