entityjs 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -0
- data/bin/entityjs +1 -1
- data/entityjs.gemspec +3 -2
- data/lib/entityjs.rb +13 -1
- data/lib/entityjs/command.rb +22 -16
- data/lib/entityjs/commands/build.rb +96 -48
- data/lib/entityjs/commands/eunit.rb +1 -1
- data/lib/entityjs/commands/html.rb +31 -0
- data/lib/entityjs/commands/min.rb +37 -0
- data/lib/entityjs/commands/new.rb +3 -0
- data/lib/entityjs/commands/server.rb +9 -3
- data/lib/entityjs/config.rb +163 -59
- data/lib/entityjs/dirc.rb +28 -0
- data/lib/entityjs/page.rb +73 -34
- data/lib/entityjs/version.rb +1 -1
- data/public/favicon.ico +0 -0
- data/public/play.html +33 -28
- data/public/qunit/qunit.entity.js +56 -6
- data/public/qunit/qunit.input.js +5 -5
- data/public/test.html +49 -0
- data/spec/javascripts/src/core/comp_spec.js +17 -6
- data/spec/javascripts/src/core/entity_spec.js +4 -5
- data/spec/javascripts/src/core/query_spec.js +25 -0
- data/spec/javascripts/src/core/re_spec.js +3 -3
- data/spec/javascripts/src/cycle/update_spec.js +8 -8
- data/spec/javascripts/src/display/align_spec.js +2 -2
- data/spec/javascripts/src/display/el_spec.js +17 -0
- data/spec/javascripts/src/input/keyboard_spec.js +4 -4
- data/spec/javascripts/src/input/mouse_spec.js +8 -7
- data/spec/javascripts/src/math/random_spec.js +8 -0
- data/spec/javascripts/src/util/clone_spec.js +20 -0
- data/spec/lib/entityjs/commands/build_spec.rb +3 -8
- data/spec/lib/entityjs/commands/html_spec.rb +27 -0
- data/spec/lib/entityjs/commands/min_spec.rb +30 -0
- data/spec/lib/entityjs/config_spec.rb +6 -0
- data/spec/lib/entityjs/page_spec.rb +4 -4
- data/src/core/comp.js +87 -31
- data/src/core/entity.js +38 -43
- data/src/core/load.js +2 -8
- data/src/core/query.js +17 -5
- data/src/core/re.js +1 -1
- data/src/core/system.js +8 -11
- data/src/cycle/drawlist.js +11 -11
- data/src/cycle/update.js +5 -5
- data/src/display/align.js +10 -3
- data/src/display/animate.js +26 -12
- data/src/display/el.js +99 -0
- data/src/input/keyboard.js +5 -5
- data/src/input/mouse.js +16 -11
- data/src/math/bisect.js +1 -1
- data/src/math/force.js +4 -4
- data/src/math/hitmap.js +2 -1
- data/src/math/iso.js +1 -1
- data/src/math/random.js +17 -2
- data/src/math/tile.js +1 -1
- data/src/media/sound.js +1 -1
- data/src/pattern/pathfind.js +2 -4
- data/src/save/storage.js +1 -1
- data/src/util/clone.js +12 -0
- data/src/util/scene.js +21 -16
- data/templates/circle/game.json +16 -0
- data/templates/circle/scripts/displays/circle.js +40 -0
- data/templates/circle/scripts/scenes/home.js +8 -17
- data/templates/circle/tests/scenes/home_test.js +3 -11
- data/templates/dom/game.json +6 -0
- data/templates/dom/scripts/els/btn.js +5 -0
- data/templates/dom/scripts/els/contain.js +5 -0
- data/templates/dom/scripts/els/header.js +5 -0
- data/templates/{animation → dom}/scripts/init.js +0 -0
- data/templates/dom/scripts/scenes/home.js +22 -0
- data/templates/{animation → dom}/scripts/scenes/load.js +0 -0
- data/templates/dom/tests/scenes/home_test.js +9 -0
- data/templates/{animation → dom}/tests/scenes/load_test.js +0 -0
- data/templates/isometric/game.json +16 -0
- data/templates/platform/game.json +18 -0
- data/templates/platform/scripts/displays/hero.js +1 -1
- data/templates/platform/scripts/items/coin.js +1 -1
- data/templates/platform/scripts/items/spring.js +1 -1
- data/templates/platform/tests/displays/hero_test.js +4 -4
- data/templates/platform/tests/factories.js +1 -1
- data/templates/platform/tests/items/coin_test.js +1 -1
- data/templates/pong/game.json +16 -0
- data/templates/tiltmaze/game.json +16 -0
- data/templates/tiltmaze/scripts/structs/level.js +1 -1
- data/templates/tiltmaze/scripts/tiles/walltile.js +1 -1
- metadata +45 -32
- data/.rspec +0 -2
- data/public/tests.html +0 -31
- data/src/cycle/worker.js +0 -9
- data/templates/animation/assets/images/hero.png +0 -0
- data/templates/animation/scripts/scenes/home.js +0 -41
- data/templates/animation/tests/init_test.js +0 -4
- data/templates/animation/tests/scenes/home_test.js +0 -15
- data/templates/arrow_keys/assets/images/arrow.png +0 -0
- data/templates/arrow_keys/config.yml +0 -22
- data/templates/arrow_keys/readme.txt +0 -9
- data/templates/arrow_keys/scripts/displays/arrow.js +0 -69
- data/templates/arrow_keys/scripts/init.js +0 -10
- data/templates/arrow_keys/scripts/inputs/controls.js +0 -35
- data/templates/arrow_keys/scripts/scenes/home.js +0 -20
- data/templates/arrow_keys/scripts/scenes/load.js +0 -57
- data/templates/arrow_keys/tests/displays/arrow_test.js +0 -29
- data/templates/arrow_keys/tests/init_test.js +0 -4
- data/templates/arrow_keys/tests/inputs/controls_test.js +0 -32
- data/templates/arrow_keys/tests/scenes/home_test.js +0 -0
- data/templates/arrow_keys/tests/scenes/load_test.js +0 -18
- data/templates/circle/config.yml +0 -22
- data/templates/isometric/config.yml +0 -22
- data/templates/platform/config.yml +0 -23
- data/templates/pong/config.yml +0 -22
- data/templates/tiltmaze/config.yml +0 -25
data/.rspec
DELETED
data/public/tests.html
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<title>EntityJS Test Suite</title>
|
|
5
|
-
|
|
6
|
-
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
|
|
7
|
-
<link rel="stylesheet" href="qunit/qunit.css" type="text/css" media="screen">
|
|
8
|
-
<style type="text/css">
|
|
9
|
-
canvas {
|
|
10
|
-
margin-left: auto;
|
|
11
|
-
margin-right: auto;
|
|
12
|
-
display: block;
|
|
13
|
-
border:1px #333 solid;
|
|
14
|
-
}
|
|
15
|
-
</style>
|
|
16
|
-
|
|
17
|
-
$JS
|
|
18
|
-
|
|
19
|
-
</head>
|
|
20
|
-
<body>
|
|
21
|
-
<h1 id="qunit-header">Game Test Suite</h1>
|
|
22
|
-
<h2 id="qunit-banner"></h2>
|
|
23
|
-
<div id="qunit-testrunner-toolbar"></div>
|
|
24
|
-
<h2 id="qunit-userAgent"></h2>
|
|
25
|
-
<ol id="qunit-tests"></ol>
|
|
26
|
-
|
|
27
|
-
<canvas width="$WIDTH" height="$HEIGHT" id="$CANVAS_ID">Browser does not support canvas element</canvas>
|
|
28
|
-
|
|
29
|
-
<a href="/">Play Game</a>
|
|
30
|
-
</body>
|
|
31
|
-
</html>
|
data/src/cycle/worker.js
DELETED
|
Binary file
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
re.scene('home')
|
|
2
|
-
.enter(function(){
|
|
3
|
-
|
|
4
|
-
re.e('sprite animate hero.png align keyboard')
|
|
5
|
-
.attr({
|
|
6
|
-
|
|
7
|
-
//sets frame size
|
|
8
|
-
sizeX:25,
|
|
9
|
-
sizeY:25,
|
|
10
|
-
|
|
11
|
-
//animation object for animate comp
|
|
12
|
-
anis:{
|
|
13
|
-
//first arg is time in milliseconds, array contains the frame numbers to flick through.
|
|
14
|
-
//time per frame = time / frames.length
|
|
15
|
-
random:[300, [0,1,2,3,4,0]]
|
|
16
|
-
},
|
|
17
|
-
|
|
18
|
-
//centers image on screen
|
|
19
|
-
alignHor:0,
|
|
20
|
-
alignVer:0
|
|
21
|
-
|
|
22
|
-
})
|
|
23
|
-
//listens for keyup event
|
|
24
|
-
.on('keyup:enter', function(){
|
|
25
|
-
//play random animation
|
|
26
|
-
this.animate('random');
|
|
27
|
-
|
|
28
|
-
console.log('Currently animating:',this.flickering());
|
|
29
|
-
})
|
|
30
|
-
.on('keyup:q', function(){
|
|
31
|
-
//ends animation
|
|
32
|
-
this.animate();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
//add help text
|
|
36
|
-
re.e('text align')
|
|
37
|
-
.text('Press enter to animate')
|
|
38
|
-
.alignTop(5)
|
|
39
|
-
.alignLeft(5);
|
|
40
|
-
|
|
41
|
-
});
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
module('home');
|
|
2
|
-
|
|
3
|
-
test('enter home', function(){
|
|
4
|
-
|
|
5
|
-
re.scene('home').enter();
|
|
6
|
-
|
|
7
|
-
var ref;
|
|
8
|
-
|
|
9
|
-
//check if entity exists
|
|
10
|
-
is(ref = re('animate').first());
|
|
11
|
-
|
|
12
|
-
//expects entity to have event listeners
|
|
13
|
-
expectEvent(ref, 'keyup:enter');
|
|
14
|
-
expectEvent(ref, 'keyup:q');
|
|
15
|
-
});
|
|
Binary file
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# Configure your game settings
|
|
2
|
-
|
|
3
|
-
width: 500
|
|
4
|
-
height: 400
|
|
5
|
-
canvas-id: game-canvas
|
|
6
|
-
|
|
7
|
-
#files to ignore in /scripts
|
|
8
|
-
scripts-ignore:
|
|
9
|
-
|
|
10
|
-
#specify files to be loaded first in /scripts
|
|
11
|
-
order:
|
|
12
|
-
|
|
13
|
-
#components to ignore in the entityjs source
|
|
14
|
-
#reduce file size by ignoring unused components
|
|
15
|
-
entity-ignore:
|
|
16
|
-
socket
|
|
17
|
-
wait
|
|
18
|
-
group
|
|
19
|
-
|
|
20
|
-
#ignore tests in /tests
|
|
21
|
-
tests-ignore:
|
|
22
|
-
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
--------------- Arrow Keys ------------
|
|
2
|
-
|
|
3
|
-
# About
|
|
4
|
-
|
|
5
|
-
This is a simple example demostrating the usefulness of EntityJS.
|
|
6
|
-
It shows how to use sprites and keyboard controls to move around arrows
|
|
7
|
-
and remove them from screen.
|
|
8
|
-
|
|
9
|
-
For more information on the engine visit www.entityjs.com
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
The arrow component is a simple sprite with key controls.
|
|
3
|
-
It listens to system updates and will move around based on WASD keys.
|
|
4
|
-
|
|
5
|
-
The image comes from arrow.png, you can view the element here, re.c('arrow.png').image.
|
|
6
|
-
|
|
7
|
-
The sprite component automatically finds the ._image on the entity and draws it to screen.
|
|
8
|
-
It will chop the image into frames based on the sizeX and sizeY values.
|
|
9
|
-
|
|
10
|
-
The original image is 80x80 and has 4 frames. So its frame sizes will be 40x40.
|
|
11
|
-
|
|
12
|
-
Switch between frames by using .frame(id), and get the current frame with .frame()
|
|
13
|
-
|
|
14
|
-
The posX and posY come from the sprite component and defines the position of the image on screen.
|
|
15
|
-
|
|
16
|
-
*/
|
|
17
|
-
re.c('arrow')
|
|
18
|
-
.requires('update sprite keyboard arrow.png')
|
|
19
|
-
.defines({
|
|
20
|
-
|
|
21
|
-
//speed of the arrow
|
|
22
|
-
speed:10,
|
|
23
|
-
//defines the sprite frame size
|
|
24
|
-
sizeX:40,
|
|
25
|
-
sizeY:40,
|
|
26
|
-
|
|
27
|
-
update:function(){
|
|
28
|
-
var s = this.speed;
|
|
29
|
-
|
|
30
|
-
//if w or up key is currently pressed..
|
|
31
|
-
if(re.pressed('w', 'up')){
|
|
32
|
-
//moves position y
|
|
33
|
-
this.posY -= s;
|
|
34
|
-
|
|
35
|
-
//change sprite frame
|
|
36
|
-
this.frame(0);
|
|
37
|
-
|
|
38
|
-
} else if(re.pressed('s', 'down')){
|
|
39
|
-
|
|
40
|
-
this.posY += s;
|
|
41
|
-
this.frame(2);
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if(re.pressed('a', 'left')){
|
|
46
|
-
|
|
47
|
-
this.posX -= s;
|
|
48
|
-
this.frame(3);
|
|
49
|
-
|
|
50
|
-
} else if(re.pressed('d', 'right')){
|
|
51
|
-
|
|
52
|
-
this.posX += s;
|
|
53
|
-
this.frame(1);
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
})
|
|
60
|
-
.init(function(){
|
|
61
|
-
|
|
62
|
-
//move to the center of the screen
|
|
63
|
-
this.posX = re.sys.sizeX * 0.5;
|
|
64
|
-
this.posY = re.sys.sizeY * 0.5;
|
|
65
|
-
|
|
66
|
-
//add event listener
|
|
67
|
-
this.on('update', this.update);
|
|
68
|
-
|
|
69
|
-
});
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
//runs when the window is finished loading
|
|
2
|
-
re.ready(function(){
|
|
3
|
-
//Starts the system and listens for inputs
|
|
4
|
-
//re.canvas in automatically defined and can be changed in config.yml
|
|
5
|
-
re.sys.init(re.canvas).start();
|
|
6
|
-
|
|
7
|
-
//enter the next scene
|
|
8
|
-
re.scene('load').enter();
|
|
9
|
-
|
|
10
|
-
});
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
The controls component listens for keyevents and creates new arrows
|
|
3
|
-
based on keys pressed.
|
|
4
|
-
*/
|
|
5
|
-
re.c('controls')
|
|
6
|
-
.requires('keyboard')
|
|
7
|
-
.defines({
|
|
8
|
-
|
|
9
|
-
addArrow:function(count){
|
|
10
|
-
count = count || 1;
|
|
11
|
-
//creates a new arrow
|
|
12
|
-
re.e('arrow', count);
|
|
13
|
-
},
|
|
14
|
-
|
|
15
|
-
removeArrow:function(){
|
|
16
|
-
//finds an entity with the arrow component and removes it.
|
|
17
|
-
re('arrow').random().dispose();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
})
|
|
21
|
-
.init(function(){
|
|
22
|
-
//called on instantiation
|
|
23
|
-
|
|
24
|
-
//listen for key events
|
|
25
|
-
//these come from the keyboard component
|
|
26
|
-
this.on({
|
|
27
|
-
'keydown:space': function(){
|
|
28
|
-
this.addArrow();
|
|
29
|
-
},
|
|
30
|
-
'keyup:r': function(){
|
|
31
|
-
this.removeArrow();
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
});
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
The home scene is the entry point of the game.
|
|
3
|
-
It creates a new controls entity and a starting arrow to play with.
|
|
4
|
-
*/
|
|
5
|
-
re.scene('home')
|
|
6
|
-
.enter(function(){
|
|
7
|
-
|
|
8
|
-
console.log('entering home scene. . .');
|
|
9
|
-
|
|
10
|
-
//add controls
|
|
11
|
-
re.controls = re.e('controls');
|
|
12
|
-
|
|
13
|
-
//add first arrow
|
|
14
|
-
re.controls.addArrow();
|
|
15
|
-
})
|
|
16
|
-
.exit(function(){
|
|
17
|
-
|
|
18
|
-
console.log('exiting home scene. . .');
|
|
19
|
-
|
|
20
|
-
})
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
The load scene loads all images and sounds in the game.
|
|
3
|
-
|
|
4
|
-
The re.assets object contains two arrays:
|
|
5
|
-
|
|
6
|
-
re.assets.images
|
|
7
|
-
re.assets.sounds
|
|
8
|
-
|
|
9
|
-
re.assets is automatically created for you and will contains whatever images
|
|
10
|
-
you place in the /assets/images or /assets/sounds directory.
|
|
11
|
-
|
|
12
|
-
You can even create new directories and place json or xml inside.
|
|
13
|
-
|
|
14
|
-
/assets/levels/level1.json
|
|
15
|
-
|
|
16
|
-
The file contents will be turned into a component and can be used on entities.
|
|
17
|
-
|
|
18
|
-
If /assets/levels/level1.json contains this:
|
|
19
|
-
|
|
20
|
-
{"something":100}
|
|
21
|
-
|
|
22
|
-
It will do this in the background for you:
|
|
23
|
-
|
|
24
|
-
re.c('level1.json level')
|
|
25
|
-
.defines({
|
|
26
|
-
something:100
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
You can even look in the source code and see for yourself.
|
|
30
|
-
|
|
31
|
-
Of course this will all be included when you run 'entityjs build'
|
|
32
|
-
|
|
33
|
-
Currently only, json, xml and tmx are supported. Tmx is a special file type used by
|
|
34
|
-
the map maker tiled and it can be used to create levels. More info: http://www.mapeditor.org/
|
|
35
|
-
|
|
36
|
-
*/
|
|
37
|
-
re.scene('load')
|
|
38
|
-
.enter(function(){
|
|
39
|
-
|
|
40
|
-
re.load(re.assets)
|
|
41
|
-
.complete(function(){
|
|
42
|
-
console.log('finished loading...');
|
|
43
|
-
|
|
44
|
-
//move to home
|
|
45
|
-
re.scene('home').enter();
|
|
46
|
-
})
|
|
47
|
-
.error(function(e){
|
|
48
|
-
console.log('Error loading asset: '+e);
|
|
49
|
-
})
|
|
50
|
-
.progress(function(i){
|
|
51
|
-
console.log('Finished loading: '+i);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
})
|
|
55
|
-
.exit(function(){
|
|
56
|
-
//exit load scene
|
|
57
|
-
})
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
module('arrow');
|
|
2
|
-
|
|
3
|
-
var arrow = re.e('arrow');
|
|
4
|
-
|
|
5
|
-
//verify the arrow has the correct sizes
|
|
6
|
-
test('size', function(){
|
|
7
|
-
|
|
8
|
-
equal(arrow.sizeX, 40);
|
|
9
|
-
equal(arrow.sizeY, 40);
|
|
10
|
-
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
test('keys move arrow', function(){
|
|
14
|
-
|
|
15
|
-
//mock/stub methods in qunit/qunit.mock.js
|
|
16
|
-
//expects 4 calls to frame
|
|
17
|
-
expectCall(arrow, 'frame', null, 4);
|
|
18
|
-
|
|
19
|
-
//simulate keypress / mouse clicks with qunit/qunit.input.js
|
|
20
|
-
//dispatches a keydown on each key, calls the given method
|
|
21
|
-
//then dispatches keyup.
|
|
22
|
-
keypress(['w', 's', 'd', 'a'], function(){
|
|
23
|
-
arrow.update();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
//by the end of the method e.frame() should have been called 4 times
|
|
27
|
-
//take a look at scripts/display/arrow.js @ update()
|
|
28
|
-
|
|
29
|
-
});
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
module('controls');
|
|
2
|
-
|
|
3
|
-
var controls = re.e('controls');
|
|
4
|
-
|
|
5
|
-
test('addArrow', function(){
|
|
6
|
-
|
|
7
|
-
var old = re('arrow').length;
|
|
8
|
-
|
|
9
|
-
//should call addarrow
|
|
10
|
-
expectCall(controls, 'addArrow');
|
|
11
|
-
|
|
12
|
-
//press space
|
|
13
|
-
keypress('space');
|
|
14
|
-
|
|
15
|
-
//check that a new arrow was created
|
|
16
|
-
equal(re('arrow').length, old+1);
|
|
17
|
-
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
test('removeArrow', function(){
|
|
21
|
-
|
|
22
|
-
var old = re('arrow').length;
|
|
23
|
-
|
|
24
|
-
expectCall(controls, 'removeArrow');
|
|
25
|
-
|
|
26
|
-
//press r
|
|
27
|
-
keypress('r');
|
|
28
|
-
|
|
29
|
-
//check that a new arrow was removed
|
|
30
|
-
equal(re('arrow').length, old-1);
|
|
31
|
-
|
|
32
|
-
});
|
|
File without changes
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
module('load');
|
|
2
|
-
|
|
3
|
-
test('load assets', function(){
|
|
4
|
-
|
|
5
|
-
//replace re.scene('home').enter with start()
|
|
6
|
-
stub(re.scene('home'), 'enter', function(){ start(); });
|
|
7
|
-
|
|
8
|
-
//go into the load scene
|
|
9
|
-
re.scene('load').enter();
|
|
10
|
-
|
|
11
|
-
//it should be loading the assets
|
|
12
|
-
//stop until it finishes and enters the new home scene
|
|
13
|
-
//the home scene should then call start()
|
|
14
|
-
stop();
|
|
15
|
-
|
|
16
|
-
expect(0);
|
|
17
|
-
|
|
18
|
-
});
|
data/templates/circle/config.yml
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# Configure your game settings
|
|
2
|
-
|
|
3
|
-
width: 500
|
|
4
|
-
height: 400
|
|
5
|
-
canvas-id: game-canvas
|
|
6
|
-
|
|
7
|
-
#files to ignore in /scripts
|
|
8
|
-
scripts-ignore:
|
|
9
|
-
|
|
10
|
-
#specify files to be loaded first in /scripts
|
|
11
|
-
order:
|
|
12
|
-
|
|
13
|
-
#components to ignore in the entityjs source
|
|
14
|
-
#reduce file size by ignoring unused components
|
|
15
|
-
entity-ignore:
|
|
16
|
-
socket
|
|
17
|
-
wait
|
|
18
|
-
group
|
|
19
|
-
|
|
20
|
-
#ignore tests in /tests
|
|
21
|
-
tests-ignore:
|
|
22
|
-
|