Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
R
renderingEngine
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
王剑峰
renderingEngine
Commits
9e0f6ab8
Commit
9e0f6ab8
authored
Nov 07, 2019
by
wjf
Browse files
Options
Browse Files
Download
Plain Diff
l
parents
2adff537
54433341
Changes
19
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
361 additions
and
108 deletions
+361
-108
render.min.js
build/render.min.js
+1
-1
render.min.js.map
build/render.min.js.map
+1
-1
game-stage.js
examples/game-stage.js
+14
-10
index.html
index.html
+57
-46
Container.ts
src/2d/display/Container.ts
+16
-10
Shape.ts
src/2d/graphics/Shape.ts
+2
-2
TextField.ts
src/2d/text/TextField.ts
+11
-0
GameStage.ts
src/zeroing/GameStage.ts
+3
-7
StackContainer.ts
src/zeroing/StackContainer.ts
+7
-0
auto-adjust.ts
src/zeroing/auto-adjust.ts
+82
-5
decorators.ts
src/zeroing/decorators.ts
+76
-0
index.ts
src/zeroing/index.ts
+1
-1
Node.ts
src/zeroing/nodes/Node.ts
+0
-13
index.ts
src/zeroing/nodes/index.ts
+0
-5
shapes.ts
src/zeroing/shapes.ts
+88
-0
DisplayObject.d.ts
types/2d/display/DisplayObject.d.ts
+0
-1
Sprite.d.ts
types/2d/display/Sprite.d.ts
+0
-2
Graphics.d.ts
types/2d/graphics/Graphics.d.ts
+0
-2
webpack.config.js
webpack.config.js
+2
-2
No files found.
build/render.min.js
View file @
9e0f6ab8
This diff is collapsed.
Click to expand it.
build/render.min.js.map
View file @
9e0f6ab8
This diff is collapsed.
Click to expand it.
examples/game-stage.js
View file @
9e0f6ab8
...
...
@@ -13,21 +13,25 @@ let stage = new render.Stage(
//启动循环
render
.
Stage
.
flushAll
();
let
node
=
new
render
.
Node
();
node
.
width
=
400
;
console
.
log
(
node
.
width
);
/*let gameStage = new render.GameStage(stage);
let
gameStage
=
new
render
.
GameStage
(
stage
);
stage
.
addChild
(
gameStage
);
let
sceneEntry
=
new
render
.
Container
();
let bg = new render.Shape();
bg.beginFill(0xFF8888);
bg.drawRect(0, 0, 300, 300);
bg.endFill();
sceneEntry
.
percentWidth
=
100
;
sceneEntry
.
percentHeight
=
100
;
let
bg
=
new
render
.
Rect
();
bg
.
percentWidth
=
100
;
bg
.
percentHeight
=
100
;
bg
.
fillColor
=
'gray'
;
sceneEntry
.
addChild
(
bg
);
gameStage.sceneContainer.push(sceneEntry);*/
let
ball
=
new
render
.
Circle
();
ball
.
radius
=
50
;
ball
.
fillColor
=
'green'
;
sceneEntry
.
addChild
(
ball
);
gameStage
.
sceneContainer
.
push
(
sceneEntry
);
/*let dialog = new render.Container();
let dbg = new render.Shape();
...
...
index.html
View file @
9e0f6ab8
...
...
@@ -2,61 +2,72 @@
<html
lang=
"en"
>
<head>
<meta
charset=
"UTF-8"
>
<title>
Document
</title>
<meta
name=
"viewport"
content=
"width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no"
/>
<meta
name=
"apple-mobile-web-app-capable"
content=
"yes"
/>
<meta
name=
"full-screen"
content=
"true"
/>
<meta
name=
"screen-orientation"
content=
"portrait"
/>
<meta
name=
"x5-fullscreen"
content=
"true"
/>
<meta
name=
"360-fullscreen"
content=
"true"
/>
<!-- <meta name="viewport" content="width=device-width,minimum-scale=1.0,user-scalable=no"> -->
<style>
html
,
body
{
padding
:
0
;
margin
:
0
;
border
:
0
;
width
:
100%
;
height
:
100%
;
overflow
:
hidden
;
position
:
absolute
;
background-color
:
white
;
}
/* .top {
width: 100%;
height: 100px;
} */
</style>
<meta
charset=
"UTF-8"
>
<title>
Document
</title>
<meta
name=
"viewport"
content=
"width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no"
/>
<meta
name=
"apple-mobile-web-app-capable"
content=
"yes"
/>
<meta
name=
"full-screen"
content=
"true"
/>
<meta
name=
"screen-orientation"
content=
"portrait"
/>
<meta
name=
"x5-fullscreen"
content=
"true"
/>
<meta
name=
"360-fullscreen"
content=
"true"
/>
<!-- <meta name="viewport" content="width=device-width,minimum-scale=1.0,user-scalable=no"> -->
<style>
html
,
body
{
padding
:
0
;
margin
:
0
;
border
:
0
;
width
:
100%
;
height
:
100%
;
overflow
:
hidden
;
position
:
absolute
;
background-color
:
white
;
}
/* .top {
width: 100%;
height: 100px;
} */
</style>
</head>
<body>
<script
type=
"text/javascript"
src=
"./build/render.min.js"
></script>
<div
id=
"cusEngine"
style=
"line-height:0;font-size:0"
></div>
<script>
<script
type=
"text/javascript"
src=
"./build/render.min.js"
></script>
<div
id=
"cusEngine"
style=
"line-height:0;font-size:0"
></div>
<script>
var
stage
=
new
render
.
Stage
(
"cusEngine"
,
750
,
1206
,
60
,
render
.
StageScaleMode
.
FIXED_WIDTH
,
render
.
RENDERER_TYPE
.
WEBGL
);
//启动循环
render
.
Stage
.
flushAll
();
stage
.
addChild
(
render
.
Sprite
.
from
(
"./11.png"
));
var
stage
=
new
render
.
Stage
(
"cusEngine"
,
750
,
1334
,
60
,
render
.
StageScaleMode
.
FIXED_WIDTH
,
render
.
RENDERER_TYPE
.
WEBGL
);
//启动循环
render
.
Stage
.
flushAll
();
</script>
let
image
=
render
.
Sprite
.
from
(
"./11.png"
);
image
.
anchorX
=
100
;
stage
.
addChild
(
image
);
/*let rect = new render.Shape();
rect.beginFill(0x00ffff);
rect.drawRect(0, 0, 100, 100);
rect.endFill();
rect.anchorX = 50;
//rect.x = 200;
//rect.y = 200;
stage.addChild(rect);*/
</script>
</body>
</body>
</html>
\ No newline at end of file
src/2d/display/Container.ts
View file @
9e0f6ab8
import
{
DisplayObject
}
from
'./DisplayObject'
;
import
{
Rectangle
}
from
"../math/Rectangle"
;
import
{
Point
}
from
'../math'
;
import
{
DisplayObject
}
from
'./DisplayObject'
;
import
{
Rectangle
}
from
"../math/Rectangle"
;
import
{
Point
}
from
'../math'
;
import
CanvasRenderer
from
'../renderers/CanvasRenderer'
;
import
{
Event
}
from
"../events/Event"
import
{
WebglRenderer
}
from
'../renderers/WebglRenderer'
;
import
{
Event
}
from
"../events/Event"
import
{
WebglRenderer
}
from
'../renderers/WebglRenderer'
;
import
{
applyAutoAdjust
}
from
"../../zeroing/auto-adjust"
;
/**
* 容器类
* @class
* @extends DisplayObject
*/
@
applyAutoAdjust
export
default
class
Container
extends
DisplayObject
{
/**
...
...
@@ -549,9 +551,11 @@ export default class Container extends DisplayObject {
// } else {
// this.scale.x = 1;
// }
if
(
this
.
_width
!==
value
)
this
.
dispatchEvent
(
Event
.
RESIZE
)
//子类有用,有_width,才需设置scaleX
this
.
_width
=
value
if
(
this
.
_width
!==
value
)
{
//子类有用,有_width,才需设置scaleX
this
.
_width
=
value
;
this
.
dispatchEvent
(
Event
.
RESIZE
);
}
}
/**
...
...
@@ -570,8 +574,10 @@ export default class Container extends DisplayObject {
// } else {
// this.scale.y = 1;
// }
if
(
this
.
_height
!==
value
)
this
.
dispatchEvent
(
Event
.
RESIZE
)
this
.
_height
=
value
if
(
this
.
_height
!==
value
)
{
this
.
_height
=
value
;
this
.
dispatchEvent
(
Event
.
RESIZE
);
}
}
//全局遍历
...
...
src/2d/graphics/Shape.ts
View file @
9e0f6ab8
...
...
@@ -327,7 +327,7 @@ export class Shape extends Sprite {
* @since 1.0.0
*/
public
beginFill
(
color
:
number
,
alpha
:
number
=
1
):
void
{
var
sColor
=
getRGBA
(
hex2string
(
color
),
alpha
);
var
sColor
=
typeof
color
===
'string'
?
color
:
getRGBA
(
hex2string
(
color
),
alpha
);
this
.
_fill
(
sColor
);
}
...
...
@@ -398,7 +398,7 @@ export class Shape extends Sprite {
miter
:
number
=
0
,
alpha
:
number
=
1
):
void
{
var
sColor
=
getRGBA
(
hex2string
(
color
),
alpha
);
var
sColor
=
typeof
color
===
'string'
?
color
:
getRGBA
(
hex2string
(
color
),
alpha
);
this
.
_stroke
(
sColor
,
lineWidth
,
cap
,
join
,
miter
);
}
...
...
src/2d/text/TextField.ts
View file @
9e0f6ab8
...
...
@@ -162,6 +162,11 @@ export class TextField extends Sprite {
// private _textHeight: number = 0;
get
width
():
number
{
if
(
this
.
_width
)
return
this
.
_width
;
this
.
updateText
();
return
this
.
scale
.
x
*
this
.
getLocalBounds
().
width
;
}
set
width
(
value
:
number
)
{
if
(
this
.
_width
!==
value
)
{
this
.
dispatchEvent
(
Event
.
RESIZE
);
...
...
@@ -169,6 +174,12 @@ export class TextField extends Sprite {
this
.
dirty
=
true
;
}
}
get
height
():
number
{
if
(
this
.
_height
)
return
this
.
_height
;
this
.
updateText
();
return
this
.
scale
.
y
*
this
.
getLocalBounds
().
height
;
}
set
height
(
value
:
number
)
{
if
(
this
.
_height
!==
value
)
{
this
.
dispatchEvent
(
Event
.
RESIZE
);
...
...
src/zeroing/GameStage.ts
View file @
9e0f6ab8
...
...
@@ -18,7 +18,9 @@ export class GameStage extends Container {
super
();
this
.
_stage
=
stage
;
stage
.
addEventListener
(
Event
.
RESIZE
,
this
.
_onStageResize
,
this
);
this
[
'percentWidth'
]
=
100
;
this
[
'percentHeight'
]
=
100
;
this
.
addChild
(
this
.
_sceneContainer
=
new
StackContainer
());
this
.
addChild
(
this
.
_popupContainer
=
new
StackContainer
());
...
...
@@ -37,10 +39,4 @@ export class GameStage extends Container {
get
popupContainer
():
StackContainer
{
return
this
.
_popupContainer
;
}
private
_onStageResize
(
e
)
{
const
{
desWidth
,
desHeight
}
=
this
.
_stage
;
this
.
width
=
this
.
_sceneContainer
.
width
=
this
.
_popupContainer
.
width
=
desWidth
;
this
.
height
=
this
.
_sceneContainer
.
height
=
this
.
_popupContainer
.
height
=
desHeight
;
}
}
src/zeroing/StackContainer.ts
View file @
9e0f6ab8
...
...
@@ -8,6 +8,13 @@ import {Container, DisplayObject} from "../2d/display";
* 栈式视图容器
*/
export
class
StackContainer
extends
Container
{
constructor
(){
super
();
this
[
'percentWidth'
]
=
100
;
this
[
'percentHeight'
]
=
100
;
}
/**
* 推入视图
* @param view
...
...
src/zeroing/auto-adjust.ts
View file @
9e0f6ab8
...
...
@@ -8,6 +8,8 @@ import {Event} from "../2d/events";
* 自适应数据
*/
export
class
AdjustData
{
percentWidth
:
number
=
NaN
;
percentHeight
:
number
=
NaN
;
left
:
number
=
NaN
;
top
:
number
=
NaN
;
right
:
number
=
NaN
;
...
...
@@ -16,26 +18,101 @@ export class AdjustData {
verticalCenter
:
number
=
NaN
;
}
function
t
(
v
)
{
return
!
isNaN
(
v
)
&&
v
!==
null
&&
v
!==
undefined
;
}
/**
* 应用自适应
* @param ctor
*/
export
function
applyAutoAdjust
(
ctor
:
Function
)
{
let
adjustData
=
new
AdjustData
();
ctor
.
prototype
.
afterConstructor
=
function
()
{
let
adjustData
=
new
AdjustData
();
this
.
__sizeDirty
=
true
;
this
.
adjustData
=
adjustData
;
this
.
addEventListener
(
Event
.
RESIZE
,
this
.
__onResize
,
this
);
this
.
addEventListener
(
Event
.
ADDED_TO_STAGE
,
this
.
__onAddedToStage
,
this
);
this
.
addEventListener
(
Event
.
REMOVED_FROM_STAGE
,
this
.
__onRemovedFromStage
,
this
);
};
ctor
.
prototype
.
__onAddedToStage
=
function
()
{
this
.
parent
.
addEventListener
(
Event
.
RESIZE
,
this
.
__onResize
,
this
);
this
.
addEventListener
(
Event
.
ENTER_FRAME
,
this
.
__onEnterFrame
,
this
);
};
ctor
.
prototype
.
__onRemovedFromStage
=
function
()
{
this
.
parent
.
removeEventListener
(
Event
.
RESIZE
,
this
.
__onResize
);
this
.
removeEventListener
(
Event
.
ENTER_FRAME
,
this
.
__onEnterFrame
);
};
ctor
.
prototype
.
__onResize
=
function
()
{
console
.
log
(
this
.
width
,
this
.
height
);
this
.
__sizeDirty
=
true
;
};
ctor
.
prototype
.
__onEnterFrame
=
function
()
{
if
(
this
.
__sizeDirty
)
{
this
.
__sizeDirty
=
false
;
const
that
=
this
;
const
{
width
:
pWidth
,
height
:
pHeight
}
=
this
.
parent
;
const
{
width
,
height
}
=
this
;
const
{
percentWidth
,
percentHeight
,
left
,
top
,
right
,
bottom
,
horizonCenter
,
verticalCenter
}
=
this
.
adjustData
;
const
applyPercentWidth
=
function
()
{
if
(
t
(
percentWidth
))
{
that
.
width
=
pWidth
*
percentWidth
/
100
;
}
};
const
applyPercentHeight
=
function
()
{
if
(
t
(
percentHeight
))
{
that
.
height
=
pHeight
*
percentHeight
/
100
;
}
};
let
pw
=
true
,
ph
=
true
;
if
(
t
(
horizonCenter
))
{
applyPercentWidth
();
this
.
x
=
(
pWidth
-
this
.
width
)
/
2
+
horizonCenter
;
}
else
{
if
(
t
(
left
))
{
this
.
x
=
left
;
if
(
t
(
right
))
{
this
.
width
=
pWidth
-
left
-
right
;
pw
=
false
;
}
}
else
if
(
t
(
right
))
{
this
.
x
=
pWidth
-
width
-
right
;
}
if
(
pw
)
{
applyPercentWidth
();
}
}
if
(
t
(
verticalCenter
))
{
applyPercentHeight
();
this
.
y
=
(
pHeight
-
this
.
height
)
/
2
+
verticalCenter
;
}
else
{
if
(
t
(
top
))
{
this
.
y
=
top
;
if
(
t
(
bottom
))
{
this
.
height
=
pHeight
-
top
-
bottom
;
ph
=
false
;
}
}
else
if
(
t
(
bottom
))
{
this
.
y
=
pHeight
-
height
-
bottom
;
}
if
(
ph
)
{
applyPercentHeight
();
}
}
}
};
for
(
let
key
in
adjustData
)
for
(
let
key
in
new
AdjustData
()
)
Object
.
defineProperty
(
ctor
.
prototype
,
key
,
{
get
:
function
()
{
return
this
.
adjustData
[
key
];
},
set
:
function
(
v
)
{
this
.
adjustData
[
key
]
=
v
;
if
(
this
.
adjustData
[
key
]
!==
v
)
{
this
.
adjustData
[
key
]
=
v
;
this
.
__sizeDirty
=
true
;
}
},
enumerable
:
true
,
configurable
:
true
...
...
src/zeroing/decorators.ts
0 → 100644
View file @
9e0f6ab8
/**
* Created by rockyl on 2018/11/9.
*
* 装饰器
*/
/**
* 属性修改时触发
* @param onModify
*/
export
function
fieldChanged
(
onModify
)
{
return
function
(
target
:
any
,
key
:
string
)
{
const
privateKey
=
'_'
+
key
;
Object
.
defineProperty
(
target
,
key
,
{
enumerable
:
true
,
get
:
function
()
{
return
this
[
privateKey
];
},
set
:
function
(
v
)
{
const
oldValue
=
this
[
privateKey
];
if
(
oldValue
!==
v
)
{
this
[
privateKey
]
=
v
;
onModify
.
apply
(
this
,
[
v
,
key
,
oldValue
]);
}
}
})
}
}
/**
* 属性变脏时设置宿主的dirty属性为true
*/
export
const
dirtyFieldDetector
=
fieldChanged
(
function
(
value
,
key
,
oldValue
)
{
this
[
'__fieldDirty'
]
=
true
;
}
);
/**
* 深度属性变脏时设置宿主的dirty属性为true
*/
export
const
deepDirtyFieldDetector
=
fieldChanged
(
function
(
value
,
key
,
oldValue
)
{
const
scope
=
this
;
scope
[
'__fieldDirty'
]
=
true
;
if
(
typeof
value
===
'object'
)
{
value
[
'onModify'
]
=
function
(){
scope
[
'__fieldDirty'
]
=
true
;
};
}
}
);
/**
* 属性变脏时触发onModify方法
*/
export
const
dirtyFieldTrigger
=
fieldChanged
(
function
(
value
,
key
,
oldValue
)
{
this
[
'onModify'
]
&&
this
[
'onModify'
](
value
,
key
,
oldValue
);
}
);
/**
* 深入属性变脏时触发onModify方法
*/
export
const
deepDirtyFieldTrigger
=
fieldChanged
(
function
(
value
:
any
,
key
,
oldValue
)
{
if
(
this
[
'onModify'
])
{
this
[
'onModify'
](
value
,
key
,
oldValue
);
if
(
typeof
value
===
'object'
)
{
value
[
'onModify'
]
=
this
[
'onModify'
];
}
}
}
);
src/zeroing/index.ts
View file @
9e0f6ab8
...
...
@@ -4,4 +4,4 @@
export
*
from
'./GameStage'
export
*
from
'./StackContainer'
export
*
from
'./nodes'
\ No newline at end of file
export
*
from
'./shapes'
src/zeroing/nodes/Node.ts
deleted
100644 → 0
View file @
2adff537
/**
* Created by rockyl on 2019-11-06.
*/
import
{
Container
}
from
"../../2d/display"
;
import
{
applyAutoAdjust
}
from
"../auto-adjust"
;
/**
* 节点
*/
@
applyAutoAdjust
export
class
Node
extends
Container
{
}
src/zeroing/nodes/index.ts
deleted
100644 → 0
View file @
2adff537
/**
* Created by rockyl on 2019-11-06.
*/
export
*
from
'./Node'
src/zeroing/shapes.ts
0 → 100644
View file @
9e0f6ab8
/**
* Created by rockyl on 2019-11-06.
*/
import
{
Shape
}
from
".."
;
import
{
Event
}
from
"../2d/events"
;
import
{
dirtyFieldDetector
}
from
"./decorators"
;
/**
* 图形基类
*/
class
ShapeBase
extends
Shape
{
protected
__fieldDirty
=
true
;
@
dirtyFieldDetector
fillColor
:
any
=
0
;
@
dirtyFieldDetector
strokeColor
:
any
=
0
;
@
dirtyFieldDetector
strokeWidth
:
number
=
0
;
constructor
()
{
super
();
this
.
addEventListener
(
Event
.
ENTER_FRAME
,
this
.
onEnterFrame
,
this
);
this
.
addEventListener
(
Event
.
RESIZE
,
this
.
onResize
,
this
);
}
private
onResize
(
e
)
{
this
.
__fieldDirty
=
true
;
}
private
onEnterFrame
(
e
)
{
if
(
this
.
__fieldDirty
)
{
this
.
__fieldDirty
=
false
;
const
{
fillColor
,
strokeColor
,
strokeWidth
}
=
this
;
this
.
clear
();
this
.
beginFill
(
fillColor
);
if
(
strokeWidth
>
0
)
{
this
.
beginStroke
(
strokeColor
,
strokeWidth
);
}
this
.
redraw
();
this
.
endFill
();
if
(
strokeWidth
>
0
)
{
this
.
endStroke
();
}
}
}
protected
redraw
()
{
}
}
/**
* 矩形
*/
export
class
Rect
extends
ShapeBase
{
@
dirtyFieldDetector
borderRadius
:
number
=
0
;
protected
redraw
()
{
const
{
width
,
height
,
borderRadius
,}
=
this
;
if
(
borderRadius
>
0
)
{
this
.
drawRoundRect
(
0
,
0
,
width
,
height
,
borderRadius
,
borderRadius
,
borderRadius
,
borderRadius
)
}
else
{
this
.
drawRect
(
0
,
0
,
width
,
height
);
}
}
}
/**
* 圆形
*/
export
class
Circle
extends
ShapeBase
{
@
dirtyFieldDetector
radius
:
number
=
0
;
protected
redraw
()
{
super
.
redraw
();
const
{
radius
}
=
this
;
this
.
drawCircle
(
radius
,
radius
,
radius
);
}
}
types/2d/display/DisplayObject.d.ts
View file @
9e0f6ab8
...
...
@@ -65,7 +65,6 @@ export declare class DisplayObject extends EventDispatcher {
mask
:
Graphics
;
width
:
number
;
height
:
number
;
protected
dispatchResizeEvent
():
void
;
private
_components
;
update
():
void
;
addComponent
(
component
:
Component
):
void
;
...
...
types/2d/display/Sprite.d.ts
View file @
9e0f6ab8
...
...
@@ -29,8 +29,6 @@ export default class Sprite extends Container {
getLocalBounds
(
rect
?:
Rectangle
):
Rectangle
;
hitTestPoint
(
globalPoint
:
Point
,
isMouseEvent
?:
boolean
):
any
;
destroy
():
void
;
width
:
number
;
height
:
number
;
anchorTexture
:
any
;
texture
:
Texture
;
static
from
(
source
:
any
):
Sprite
;
...
...
types/2d/graphics/Graphics.d.ts
View file @
9e0f6ab8
...
...
@@ -81,8 +81,6 @@ export default class Graphics extends Container {
updateLocalBoundsSelf
():
void
;
private
generateCanvasTexture
;
destroy
():
void
;
width
:
number
;
height
:
number
;
private
proccessHoles
;
private
addUvs
;
private
adjustUvs
;
...
...
webpack.config.js
View file @
9e0f6ab8
...
...
@@ -24,11 +24,11 @@ module.exports = {
},
devtool
:
'source-map'
,
plugins
:
[
new
UglifyJSPlugin
(
/*
new UglifyJSPlugin(
{ sourceMap: true }
), new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
})
*/
],
resolve
:
{
extensions
:
[
'.webpack.js'
,
'.web.js'
,
'.ts'
,
'.js'
]
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment