js:add es5 compile option

This commit is contained in:
pengfei.zhou
2020-01-17 10:12:13 +08:00
committed by osborn
parent 7f089b6bcd
commit 83a9b79073
15 changed files with 881 additions and 16519 deletions

View File

@@ -0,0 +1,371 @@
/*
* Copyright [2019] [Doric.Pub]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { uniqueId } from "../util/uniqueId";
import { loge } from "../util/log";
import "reflect-metadata"
/**
* ``` TypeScript
* // load script in global scope
* Reflect.apply(
* function(doric,context,Entry,require){
* //Script content
* REG()
* },doric.jsObtainContext(id),[
* undefined,
* doric.jsObtainContext(id),
* doric.jsObtainEntry(id),
* doric.__require__,
* ])
* // load module in global scope
* Reflect.apply(doric.jsRegisterModule,this,[
* moduleName,
* Reflect.apply(function(__module){
* (function(module,exports,require){
* //module content
* })(__module,__module.exports,doric.__require__);
* return __module.exports
* },this,[{exports:{}}])
* ])
*
* ```
*/
declare function nativeRequire(moduleName: string): boolean
declare function nativeBridge(contextId: string, namespace: string, method: string, callbackId?: string, args?: any): boolean
declare function nativeSetTimer(timerId: number, interval: number, repeat: boolean): void
declare function nativeClearTimer(timerId: number): void
function hookBeforeNativeCall(context?: Context) {
if (context) {
Reflect.defineMetadata('__doric_context__', context, global)
context.hookBeforeNativeCall()
}
}
function hookAfterNativeCall(context?: Context) {
if (context) {
context.hookAfterNativeCall()
}
}
function getContext(): Context | undefined {
return Reflect.getMetadata('__doric_context__', global)
}
function setContext(context?: Context) {
Reflect.defineMetadata('__doric_context__', context, global)
}
export function jsCallResolve(contextId: string, callbackId: string, args?: any) {
const context = gContexts.get(contextId)
if (context === undefined) {
loge(`Cannot find context for context id:${contextId}`)
return
}
const callback = context.callbacks.get(callbackId)
if (callback === undefined) {
loge(`Cannot find call for context id:${contextId},callback id:${callbackId}`)
return
}
const argumentsList: any = []
for (let i = 2; i < arguments.length; i++) {
argumentsList.push(arguments[i])
}
hookBeforeNativeCall(context)
Reflect.apply(callback.resolve, context, argumentsList)
hookAfterNativeCall(context)
}
export function jsCallReject(contextId: string, callbackId: string, args?: any) {
const context = gContexts.get(contextId)
if (context === undefined) {
loge(`Cannot find context for context id:${contextId}`)
return
}
const callback = context.callbacks.get(callbackId)
if (callback === undefined) {
loge(`Cannot find call for context id:${contextId},callback id:${callbackId}`)
return
}
const argumentsList: any = []
for (let i = 2; i < arguments.length; i++) {
argumentsList.push(arguments[i])
}
hookBeforeNativeCall(context)
Reflect.apply(callback.reject, context.entity, argumentsList)
hookAfterNativeCall(context)
}
export class Context {
entity: any
id: string
callbacks: Map<string, { resolve: Function, reject: Function }> = new Map
hookBeforeNativeCall() {
if (this.entity && Reflect.has(this.entity, 'hookBeforeNativeCall')) {
Reflect.apply(Reflect.get(this.entity, 'hookBeforeNativeCall'), this.entity, [])
}
}
hookAfterNativeCall() {
if (this.entity && Reflect.has(this.entity, 'hookAfterNativeCall')) {
Reflect.apply(Reflect.get(this.entity, 'hookAfterNativeCall'), this.entity, [])
}
}
constructor(id: string) {
this.id = id
return new Proxy(this, {
get: (target, p: any) => {
if (Reflect.has(target, p)) {
return Reflect.get(target, p)
} else {
const namespace = p
return new Proxy({}, {
get: (target, p: any) => {
if (Reflect.has(target, p)) {
return Reflect.get(target, p)
} else {
const context = this
return function () {
const args = []
args.push(namespace)
args.push(p)
for (let arg of arguments) {
args.push(arg)
}
return Reflect.apply(context.callNative, context, args)
}
}
}
})
}
}
})
}
callNative(namespace: string, method: string, args?: any): Promise<any> {
const callbackId = uniqueId('callback')
nativeBridge(this.id, namespace, method, callbackId, args)
return new Promise((resolve, reject) => {
this.callbacks.set(callbackId, {
resolve,
reject,
})
})
}
register(instance: Object) {
this.entity = instance
}
function2Id(func: Function) {
const functionId = uniqueId('function')
this.callbacks.set(functionId, {
resolve: func,
reject: () => { loge("This should not be called") }
})
return functionId
}
removeFuncById(funcId: string) {
this.callbacks.delete(funcId)
}
}
const gContexts: Map<string, Context> = new Map
const gModules: Map<string, any> = new Map
export function jsObtainContext(id: string) {
if (gContexts.has(id)) {
const context = gContexts.get(id)
setContext(context)
return context
} else {
const context: Context = new Context(id)
gContexts.set(id, context)
setContext(context)
return context
}
}
export function jsReleaseContext(id: string) {
const context = gContexts.get(id)
const args = arguments
if (context) {
timerInfos.forEach((v, k) => {
if (v.context === context) {
if (global.nativeClearTimer === undefined) {
return Reflect.apply(_clearTimeout, undefined, args)
}
timerInfos.delete(k)
nativeClearTimer(k)
}
})
}
gContexts.delete(id)
}
export function __require__(name: string): any {
if (gModules.has(name)) {
return gModules.get(name)
} else {
if (nativeRequire(name)) {
return gModules.get(name)
} else {
return undefined
}
}
}
export function jsRegisterModule(name: string, moduleObject: any) {
gModules.set(name, moduleObject)
}
export function jsCallEntityMethod(contextId: string, methodName: string, args?: any) {
const context = gContexts.get(contextId)
if (context === undefined) {
loge(`Cannot find context for context id:${contextId}`)
return
}
if (context.entity === undefined) {
loge(`Cannot find holder for context id:${contextId}`)
return
}
if (Reflect.has(context.entity, methodName)) {
const argumentsList: any = []
for (let i = 2; i < arguments.length; i++) {
argumentsList.push(arguments[i])
}
hookBeforeNativeCall(context)
const ret = Reflect.apply(Reflect.get(context.entity, methodName), context.entity, argumentsList)
hookAfterNativeCall(context)
return ret
} else {
loge(`Cannot find method for context id:${contextId},method name is:${methodName}`)
}
}
export function jsObtainEntry(contextId: string) {
const context = jsObtainContext(contextId)
return <T extends { new(...args: any[]): {} }>(constructor: T) => {
const ret = class extends constructor {
context = context
}
if (context) {
context.register(new ret)
}
return ret
}
}
const global = Function('return this')()
let __timerId__ = 0
const timerInfos: Map<number, { callback: () => void, context?: Context }> = new Map
const _setTimeout = global.setTimeout
const _setInterval = global.setInterval
const _clearTimeout = global.clearTimeout
const _clearInterval = global.clearInterval
const doricSetTimeout = function (handler: Function, timeout?: number | undefined, ...args: any[]) {
if (global.nativeSetTimer === undefined) {
return Reflect.apply(_setTimeout, undefined, arguments)
}
const id = __timerId__++
timerInfos.set(id, {
callback: () => {
Reflect.apply(handler, undefined, args)
timerInfos.delete(id)
},
context: getContext(),
})
nativeSetTimer(id, timeout || 0, false)
return id
}
const doricSetInterval = function (handler: Function, timeout?: number | undefined, ...args: any[]) {
if (global.nativeSetTimer === undefined) {
return Reflect.apply(_setInterval, undefined, arguments)
}
const id = __timerId__++
timerInfos.set(id, {
callback: () => {
Reflect.apply(handler, undefined, args)
},
context: getContext(),
})
nativeSetTimer(id, timeout || 0, true)
return id
}
const doricClearTimeout = function (timerId: number) {
if (global.nativeClearTimer === undefined) {
return Reflect.apply(_clearTimeout, undefined, arguments)
}
timerInfos.delete(timerId)
nativeClearTimer(timerId)
}
const doricClearInterval = function (timerId: number) {
if (global.nativeClearTimer === undefined) {
return Reflect.apply(_clearInterval, undefined, arguments)
}
timerInfos.delete(timerId)
nativeClearTimer(timerId)
}
if (!global.setTimeout) {
global.setTimeout = doricSetTimeout
} else {
global.doricSetTimeout = doricSetTimeout
}
if (!global.setInterval) {
global.setInterval = doricSetInterval
} else {
global.doricSetInterval = doricSetInterval
}
if (!global.clearTimeout) {
global.clearTimeout = doricClearTimeout
} else {
global.doricClearTimeout = doricClearTimeout
}
if (!global.clearInterval) {
global.clearInterval = doricClearInterval
} else {
global.doricClearInterval = doricClearInterval
}
export function jsCallbackTimer(timerId: number) {
const timerInfo = timerInfos.get(timerId)
if (timerInfo === undefined) {
return
}
if (timerInfo.callback instanceof Function) {
hookBeforeNativeCall(timerInfo.context)
Reflect.apply(timerInfo.callback, timerInfo.context, [])
hookAfterNativeCall(timerInfo.context)
}
}

View File

@@ -0,0 +1,18 @@
/*
* Copyright [2019] [Doric.Pub]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './view.es5'
export * from './panel'
export * from './animation'

408
doric-js/src/ui/view.es5.ts Normal file
View File

@@ -0,0 +1,408 @@
/*
* Copyright [2019] [Doric.Pub]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Color, GradientColor } from "../util/color"
import { Modeling, Model, obj2Model } from "../util/types";
import { uniqueId } from "../util/uniqueId";
import { loge } from "../util/log";
import { BridgeContext } from "../runtime/global";
import { LayoutConfig } from '../util/layoutconfig'
import { IAnimation } from "./animation";
export function Property(target: Object, propKey: string) {
Reflect.defineMetadata(propKey, true, target)
}
export interface IView {
width?: number
height?: number
backgroundColor?: Color | GradientColor
corners?: number | { leftTop?: number; rightTop?: number; leftBottom?: number; rightBottom?: number }
border?: { width: number; color: Color; }
shadow?: { color: Color; opacity: number; radius: number; offsetX: number; offsetY: number }
/**
* float [0,..1]
*/
alpha?: number
hidden?: boolean
padding?: {
left?: number,
right?: number,
top?: number,
bottom?: number,
}
layoutConfig?: LayoutConfig
onClick?: Function
identifier?: string
/**++++++++++transform++++++++++*/
translationX?: number
translationY?: number
scaleX?: number
scaleY?: number
/**
* float [0,..1]
*/
pivotX?: number
/**
* float [0,..1]
*/
pivotY?: number
/**
* rotation*PI
*/
rotation?: number
/**----------transform----------*/
}
export type NativeViewModel = {
id: string;
type: string;
props: {
[index: string]: Model;
};
}
export abstract class View implements Modeling, IView {
@Property
width: number = 0
@Property
height: number = 0
@Property
x: number = 0
@Property
y: number = 0
@Property
backgroundColor?: Color | GradientColor
@Property
corners?: number | { leftTop?: number; rightTop?: number; leftBottom?: number; rightBottom?: number }
@Property
border?: { width: number; color: Color; }
@Property
shadow?: { color: Color; opacity: number; radius: number; offsetX: number; offsetY: number }
@Property
alpha?: number
@Property
hidden?: boolean
@Property
viewId = uniqueId('ViewId')
@Property
padding?: {
left?: number,
right?: number,
top?: number,
bottom?: number,
}
@Property
layoutConfig?: LayoutConfig
@Property
onClick?: Function
superview?: Superview
callbacks: Map<String, Function> = new Map
private callback2Id(f: Function) {
const id = uniqueId('Function')
this.callbacks.set(id, f)
return id
}
private id2Callback(id: string) {
let f = this.callbacks.get(id)
if (f === undefined) {
f = Reflect.get(this, id) as Function
}
return f
}
constructor() {
return new Proxy(this, {
get: (target, p, receiver) => {
return Reflect.get(target, p, receiver)
},
set: (target, p, v, receiver) => {
const oldV = Reflect.get(target, p, receiver)
const ret = Reflect.set(target, p, v, receiver)
if (Reflect.getMetadata(p, target) && oldV !== v) {
receiver.onPropertyChanged(p.toString(), oldV, v)
}
return ret
}
})
}
/** Anchor start*/
get left() {
return this.x
}
set left(v: number) {
this.x = v
}
get right() {
return this.x + this.width
}
set right(v: number) {
this.x = v - this.width
}
get top() {
return this.y
}
set top(v: number) {
this.y = v
}
get bottom() {
return this.y + this.height
}
set bottom(v: number) {
this.y = v - this.height
}
get centerX() {
return this.x + this.width / 2
}
get centerY() {
return this.y + this.height / 2
}
set centerX(v: number) {
this.x = v - this.width / 2
}
set centerY(v: number) {
this.y = v - this.height / 2
}
/** Anchor end*/
private __dirty_props__: { [index: string]: Model | undefined } = {}
get dirtyProps() {
return this.__dirty_props__
}
nativeViewModel: NativeViewModel = {
id: this.viewId,
type: this.constructor.name,
props: this.__dirty_props__,
}
onPropertyChanged(propKey: string, oldV: Model, newV: Model): void {
if (newV instanceof Function) {
newV = this.callback2Id(newV)
} else {
newV = obj2Model(newV)
}
this.__dirty_props__[propKey] = newV
}
clean() {
for (const key in this.__dirty_props__) {
if (Reflect.has(this.__dirty_props__, key)) {
Reflect.deleteProperty(this.__dirty_props__, key)
}
}
}
isDirty() {
return Reflect.ownKeys(this.__dirty_props__).length !== 0
}
responseCallback(id: string, ...args: any) {
const f = this.id2Callback(id)
if (f instanceof Function) {
const argumentsList: any = []
for (let i = 1; i < arguments.length; i++) {
argumentsList.push(arguments[i])
}
return Reflect.apply(f, this, argumentsList)
} else {
loge(`Cannot find callback:${id} for ${JSON.stringify(this.toModel())}`)
}
}
toModel() {
return this.nativeViewModel
}
let(block: (it: this) => void) {
block(this)
}
also(block: (it: this) => void) {
block(this)
return this
}
apply(config: IView) {
for (let key in config) {
Reflect.set(this, key, Reflect.get(config, key, config), this)
}
return this
}
in(group: Group) {
group.addChild(this)
return this
}
nativeChannel(context: any, name: string) {
let thisView: View | undefined = this
return function (args: any = undefined) {
const func = context.shader.command
const viewIds = []
while (thisView != undefined) {
viewIds.push(thisView.viewId)
thisView = thisView.superview
}
const params = {
viewIds: viewIds.reverse(),
name,
args,
}
return Reflect.apply(func, undefined, [params]) as Promise<any>
}
}
getWidth(context: BridgeContext) {
return this.nativeChannel(context, 'getWidth')() as Promise<number>
}
getHeight(context: BridgeContext) {
return this.nativeChannel(context, 'getHeight')() as Promise<number>
}
getLocationOnScreen(context: BridgeContext) {
return this.nativeChannel(context, "getLocationOnScreen")() as Promise<{ x: number, y: number }>
}
/**++++++++++transform++++++++++*/
@Property
translationX?: number
@Property
translationY?: number
@Property
scaleX?: number
@Property
scaleY?: number
@Property
pivotX?: number
@Property
pivotY?: number
@Property
rotation?: number
/**----------transform----------*/
doAnimation(context: BridgeContext, animation: IAnimation) {
return this.nativeChannel(context, "doAnimation")(animation.toModel()).then((args) => {
for (let key in args) {
Reflect.set(this, key, Reflect.get(args, key, args), this)
Reflect.deleteProperty(this.__dirty_props__, key)
}
})
}
}
export abstract class Superview extends View {
subviewById(id: string): View | undefined {
for (let v of this.allSubviews()) {
if (v.viewId === id) {
return v
}
}
}
abstract allSubviews(): Iterable<View>
isDirty() {
if (super.isDirty()) {
return true
} else {
for (const v of this.allSubviews()) {
if (v.isDirty()) {
return true
}
}
}
return false
}
clean() {
for (let v of this.allSubviews()) {
v.clean()
}
super.clean()
}
toModel() {
const subviews = []
for (let v of this.allSubviews()) {
if (v != undefined) {
v.superview = this
if (v.isDirty()) {
subviews.push(v.toModel())
}
}
}
this.dirtyProps.subviews = subviews
return super.toModel()
}
}
export abstract class Group extends Superview {
readonly children: View[] = new Proxy([], {
set: (target, index, value) => {
const ret = Reflect.set(target, index, value)
// Let getDirty return true
this.dirtyProps.children = this.children.map(e => e.viewId)
return ret
}
})
allSubviews() {
return this.children
}
addChild(view: View) {
this.children.push(view)
}
}