├── .gitignore
├── CODE_OF_CONDUCT.md
├── README.md
├── comparsion-css-sass-gsap
├── css
│ └── style.css
├── index.haml
├── index.html
├── js
│ └── index.js
└── scss
│ └── style.scss
├── gsap-basic-timeline.js
├── gsap-cheatsheet.js
├── gsap-simple-tween
├── css
│ └── style.css
├── index.html
└── js
│ └── index.js
├── gsap-two-tweens
├── css
│ └── style.css
├── index.haml
├── index.html
├── js
│ └── index.js
└── scss
│ └── style.scss
└── sample-svgs
├── battery.svg
├── black-and-white
├── balloon.svg
├── carousel.svg
├── dog1.svg
├── dog2.svg
├── flower.svg
├── gears.svg
├── gears2.svg
├── lightbulb2.svg
├── lightbulbs.svg
├── pie.svg
├── sleigh.svg
└── snowflake.svg
└── color
├── 1466065151_travel_journey-16.svg
├── 1466065154_travel_journey-04.svg
├── 1466065160_travel_journey-06.svg
├── 1466065173_travel_journey-12.svg
├── 1466065248_Drinks-30.svg
├── 1466065253_Drinks-32.svg
├── 1466065266_medicine_care_treatment_hospital_recovery-66.svg
├── 1466065274_medicine_care_treatment_hospital_recovery-30.svg
└── 1466065282_medicine_care_treatment_hospital_recovery-64.svg
/.gitignore:
--------------------------------------------------------------------------------
1 | ### OSX ###
2 | *.DS_Store
3 | .AppleDouble
4 | .LSOverride
5 |
6 | # Icon must end with two \r
7 | Icon
8 | # Thumbnails
9 | ._*
10 | # Files that might appear in the root of a volume
11 | .DocumentRevisions-V100
12 | .fseventsd
13 | .Spotlight-V100
14 | .TemporaryItems
15 | .Trashes
16 | .VolumeIcon.icns
17 | .com.apple.timemachine.donotpresent
18 | # Directories potentially created on remote AFP share
19 | .AppleDB
20 | .AppleDesktop
21 | Network Trash Folder
22 | Temporary Items
23 | .apdisk
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | ## 1. Purpose
4 |
5 | A primary goal of Workshop Resources is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof).
6 |
7 | This code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior.
8 |
9 | We invite all those who participate in Workshop Resources to help us create safe and positive experiences for everyone.
10 |
11 | ## 2. Open Source Citizenship
12 |
13 | A supplemental goal of this Code of Conduct is to increase open source citizenship by encouraging participants to recognize and strengthen the relationships between our actions and their effects on our community.
14 |
15 | Communities mirror the societies in which they exist and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society.
16 |
17 | If you see someone who is making an extra effort to ensure our community is welcoming, friendly, and encourages all participants to contribute to the fullest extent, we want to know.
18 |
19 | ## 3. Expected Behavior
20 |
21 | The following behaviors are expected and requested of all community members:
22 |
23 | * Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community.
24 | * Exercise consideration and respect in your speech and actions.
25 | * Attempt collaboration before conflict.
26 | * Refrain from demeaning, discriminatory, or harassing behavior and speech.
27 | * Be mindful of your surroundings and of your fellow participants. Alert community leaders if you notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if they seem inconsequential.
28 | * Remember that community event venues may be shared with members of the public; please be respectful to all patrons of these locations.
29 |
30 | ## 4. Unacceptable Behavior
31 |
32 | The following behaviors are considered harassment and are unacceptable within our community:
33 |
34 | * Violence, threats of violence or violent language directed against another person.
35 | * Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory jokes and language.
36 | * Posting or displaying sexually explicit or violent material.
37 | * Posting or threatening to post other people’s personally identifying information ("doxing").
38 | * Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability.
39 | * Inappropriate photography or recording.
40 | * Inappropriate physical contact. You should have someone’s consent before touching them.
41 | * Unwelcome sexual attention. This includes, sexualized comments or jokes; inappropriate touching, groping, and unwelcomed sexual advances.
42 | * Deliberate intimidation, stalking or following (online or in person).
43 | * Advocating for, or encouraging, any of the above behavior.
44 | * Sustained disruption of community events, including talks and presentations.
45 |
46 | ## 5. Consequences of Unacceptable Behavior
47 |
48 | Unacceptable behavior from any community member, including sponsors and those with decision-making authority, will not be tolerated.
49 |
50 | Anyone asked to stop unacceptable behavior is expected to comply immediately.
51 |
52 | If a community member engages in unacceptable behavior, the community organizers may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning (and without refund in the case of a paid event).
53 |
54 | ## 6. Reporting Guidelines
55 |
56 | If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible. info@webanimationworkshops.com.
57 |
58 |
59 |
60 | Additionally, community organizers are available to help community members engage with local law enforcement or to otherwise help those experiencing unacceptable behavior feel safe. In the context of in-person events, organizers will also provide escorts as desired by the person experiencing distress.
61 |
62 | ## 7. Addressing Grievances
63 |
64 | If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify Web Animation Workshops with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies.
65 |
66 |
67 |
68 | ## 8. Scope
69 |
70 | We expect all community participants (contributors, paid or otherwise; sponsors; and other guests) to abide by this Code of Conduct in all community venues–online and in-person–as well as in all one-on-one communications pertaining to community business.
71 |
72 | This code of conduct and its related procedures also applies to unacceptable behavior occurring outside the scope of community activities when such behavior has the potential to adversely affect the safety and well-being of community members.
73 |
74 | ## 9. Contact info
75 |
76 | info@webanimationworkshops.com
77 |
78 | ## 10. License and attribution
79 |
80 | This Code of Conduct is distributed under a [Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/).
81 |
82 | Portions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/) and the [Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy).
83 |
84 | Retrieved on November 22, 2016 from [http://citizencodeofconduct.org/](http://citizencodeofconduct.org/)
85 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Web Animation Workshops Resources
2 |
3 | This repo houses resources for Web Animation Workshops
4 |
5 | ### Authors: Sarah Drasner and Val Head
6 |
7 | We'll be using CodePen for both the teaching demos and the homework so that we can get you up and running faster. For sections covering React-Motion, it may make more sense to use Create-React-App so that your workflow more closely mirrors development, but that decision is up to you.
8 |
9 | _Password to all decks is WAW!_
10 |
11 | ## Slides:
12 |
13 | * [Introduction](http://slides.com/vlh/waw-intro?token=eE_f3_XU)
14 | * [Classic animation principles worth stealing](https://www.slideshare.net/secret/1yK01w1F4bNpNz)
15 | * [Principles of UI/UX animation](http://slides.com/sdrasner/waw-principles-ux?token=258_EYo8)
16 | * [Essentials of CSS animation](http://slides.com/vlh/waw-cssanimation?token=emxTzAcD)
17 | * [Basics of TweenMax & TimelineMax](http://slides.com/sdrasner/waw-gsap?token=rg606T3G)
18 | * [SVG workflow and optimization](http://slides.com/vlh/waw-svgworkflow?token=V4aSNC9y)
19 | * [SVG animation](http://slides.com/sdrasner/waw-svg-animation?token=D-wwuju5)
20 | * [Intro to Animating with React](http://slides.com/vlh/intro-anim-react?token=CNlmb06B#/)
21 | * [Animating with React cont.](http://slides.com/sdrasner/waw-react?token=Pmgv8l9k)
22 |
23 | ## Further Slide Resources:
24 |
25 | * [Web Animation Performance](http://slides.com/vlh/waw-webanimationperf?token=3xSwGsW5)
26 | * [The Web Animation API](http://slides.com/vlh/waw-waapi?token=wz6rRkTn)
27 | * [Mo.js](http://slides.com/sdrasner/svg-workshop-mojs?token=wAkiI-Pe)
28 |
29 | ## CodePen Collections:
30 |
31 | * [CSS Excercises](http://codepen.io/collection/DBLaex/)
32 | * [SVG Animation and GreenSock](http://codepen.io/collection/XvBQJQ/)
33 |
34 | ## Our Twitter Contact Info
35 |
36 | * [Sarah Drasner](https://twitter.com/sarah_edo)
37 | * [Val Head](https://twitter.com/vlh)
38 |
39 | ## License
40 |
41 | [](http://creativecommons.org/licenses/by-nc-sa/4.0/)
42 |
43 | This work is licensed under a [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-nc-sa/4.0/)
44 |
--------------------------------------------------------------------------------
/comparsion-css-sass-gsap/css/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | background: #333;
3 | }
4 |
5 | .bar {
6 | margin: 20px;
7 | background: red;
8 | width: 40px;
9 | height: 20px;
10 | }
11 |
12 | .css, .sass, .gsap {
13 | float: left;
14 | }
15 |
16 | .css {
17 | margin-left: 40%;
18 | }
19 |
20 | @-webkit-keyframes staggerFoo {
21 | to {
22 | background: orange;
23 | -webkit-transform: rotate(90deg);
24 | transform: rotate(90deg);
25 | }
26 | }
27 |
28 | @keyframes staggerFoo {
29 | to {
30 | background: orange;
31 | -webkit-transform: rotate(90deg);
32 | transform: rotate(90deg);
33 | }
34 | }
35 | .css .bar:nth-child(1) {
36 | -webkit-animation: staggerFoo 1s 0.1s ease-out both;
37 | animation: staggerFoo 1s 0.1s ease-out both;
38 | }
39 |
40 | .css .bar:nth-child(2) {
41 | -webkit-animation: staggerFoo 1s 0.2s ease-out both;
42 | animation: staggerFoo 1s 0.2s ease-out both;
43 | }
44 |
45 | .css .bar:nth-child(3) {
46 | -webkit-animation: staggerFoo 1s 0.3s ease-out both;
47 | animation: staggerFoo 1s 0.3s ease-out both;
48 | }
49 |
50 | .css .bar:nth-child(4) {
51 | -webkit-animation: staggerFoo 1s 0.4s ease-out both;
52 | animation: staggerFoo 1s 0.4s ease-out both;
53 | }
54 |
55 | .css .bar:nth-child(5) {
56 | -webkit-animation: staggerFoo 1s 0.5s ease-out both;
57 | animation: staggerFoo 1s 0.5s ease-out both;
58 | }
59 |
60 | .css .bar:nth-child(6) {
61 | -webkit-animation: staggerFoo 1s 0.5s ease-out both;
62 | animation: staggerFoo 1s 0.5s ease-out both;
63 | }
64 |
65 | .sass .bar:nth-child(1) {
66 | -webkit-animation: staggerFoo 1s 1.1s ease-out both;
67 | animation: staggerFoo 1s 1.1s ease-out both;
68 | }
69 |
70 | .sass .bar:nth-child(2) {
71 | -webkit-animation: staggerFoo 1s 1.2s ease-out both;
72 | animation: staggerFoo 1s 1.2s ease-out both;
73 | }
74 |
75 | .sass .bar:nth-child(3) {
76 | -webkit-animation: staggerFoo 1s 1.3s ease-out both;
77 | animation: staggerFoo 1s 1.3s ease-out both;
78 | }
79 |
80 | .sass .bar:nth-child(4) {
81 | -webkit-animation: staggerFoo 1s 1.4s ease-out both;
82 | animation: staggerFoo 1s 1.4s ease-out both;
83 | }
84 |
85 | .sass .bar:nth-child(5) {
86 | -webkit-animation: staggerFoo 1s 1.5s ease-out both;
87 | animation: staggerFoo 1s 1.5s ease-out both;
88 | }
89 |
90 | .sass .bar:nth-child(6) {
91 | -webkit-animation: staggerFoo 1s 1.6s ease-out both;
92 | animation: staggerFoo 1s 1.6s ease-out both;
93 | }
94 |
95 | h2 {
96 | text-align: center;
97 | color: #666;
98 | }
99 |
--------------------------------------------------------------------------------
/comparsion-css-sass-gsap/index.haml:
--------------------------------------------------------------------------------
1 | .css
2 | %h2 CSS
3 | - @x = 5
4 | - @x.times do
5 | .bar
6 |
7 | .sass
8 | %h2 SASS
9 | - @x = 5
10 | - @x.times do
11 | .bar
12 |
13 | .gsap
14 | %h2 GSAP
15 | - @x = 5
16 | - @x.times do
17 | .bar
--------------------------------------------------------------------------------
/comparsion-css-sass-gsap/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | A Pen by Sarah Drasner
7 |
8 |
9 |
10 |
11 |
12 |
13 |
CSS
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
SASS
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
GSAP
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/comparsion-css-sass-gsap/js/index.js:
--------------------------------------------------------------------------------
1 | TweenMax.staggerTo(".gsap .bar", 1, { backgroundColor: "orange", rotation: 90, delay: 2, ease: Sine.easeOut}, 0.1);
--------------------------------------------------------------------------------
/comparsion-css-sass-gsap/scss/style.scss:
--------------------------------------------------------------------------------
1 | html {
2 | background: #333;
3 | }
4 |
5 | .bar {
6 | margin: 20px;
7 | background: red;
8 | width: 40px;
9 | height: 20px;
10 | }
11 |
12 | .css, .sass, .gsap {
13 | float: left;
14 | }
15 |
16 | .css {
17 | margin-left: 40%;
18 | }
19 |
20 | @keyframes staggerFoo {
21 | to {
22 | background: orange;
23 | transform: rotate(90deg);
24 | }
25 | }
26 |
27 | .css .bar:nth-child(1) { animation: staggerFoo 1s 0.1s ease-out both; }
28 | .css .bar:nth-child(2) { animation: staggerFoo 1s 0.2s ease-out both; }
29 | .css .bar:nth-child(3) { animation: staggerFoo 1s 0.3s ease-out both; }
30 | .css .bar:nth-child(4) { animation: staggerFoo 1s 0.4s ease-out both; }
31 | .css .bar:nth-child(5) { animation: staggerFoo 1s 0.5s ease-out both; }
32 | .css .bar:nth-child(6) { animation: staggerFoo 1s 0.5s ease-out both; }
33 |
34 | @for $i from 1 through 6 {
35 | .sass .bar:nth-child(#{$i} ) {
36 | animation: staggerFoo 1s 1+($i * 0.1s) ease-out both;
37 | }
38 | }
39 |
40 | h2 {
41 | text-align: center;
42 | color: #666;
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/gsap-basic-timeline.js:
--------------------------------------------------------------------------------
1 | //set properties needed for animation
2 | TweenMax.set(".element", {
3 | perspective: 400
4 | });
5 |
6 | // the first scene
7 | function sceneOne() {
8 | var tl = new TimelineMax();
9 |
10 | tl.add("label");
11 | //animation for element on timeline
12 | tl.to(".other-element", 1, {property: value}, "label");
13 | //stagger needs the 0.2, which tells the elements how long to wait between each next fire
14 | tl.staggerTo(".next-element li", 1, {property: value}, 0.2, "label+=1");
15 |
16 | //make the timeline go a little bit faster
17 | tl.timeScale(1.2);
18 |
19 | return tl;
20 | }
21 |
22 | // Create a master timeline
23 | var master = new TimelineMax({options});
24 | // Add the scene function to the master
25 | master.add(sceneOne(), "labelOnMaster");
26 |
27 | //use this while you're working to get to a place in time
28 | //master.seek("labelOnMaster+=2");e
--------------------------------------------------------------------------------
/gsap-cheatsheet.js:
--------------------------------------------------------------------------------
1 | // Position parameters
2 |
3 | var tl = new TimelineLite();
4 | tl.to(".orange", 1, {x:750})
5 | //this just follows the first
6 | .to(".green", 1, {x:750})
7 | //there is a one second gap between these two tweens
8 | .to(".blue", 1, {x:750}, "+=1")
9 | //this goes to two seconds in
10 | .to(".red", 1, {x:750}, "2")
11 | // add a relative label at this part of the timeline
12 | .add("newLabel")
13 | // tween at 3 seconds past the relative label
14 | .to(".purple", 1, {x:750}, "newlabel+=3");
15 |
16 | //-------------------------------------------------------
17 |
18 | // Some Useful Timeline function calls
19 |
20 | tl.pause(); // Pause timeline - within a timeline it would be var foo = new TimelineMax({paused:true});, very handy for getting a timeline ready for interaction.
21 | tl.restart(); // Restart the timeline - very useful for interaction (set the timeline to paused:true, and then trigger restart)
22 | tl.reverse(); // Reverse playback- nice for exiting a scene in interaction.
23 | tl.seek(n); // Go to n seconds or 'label'- good for working because you don't have to sit through the whole animation.
24 | tl.timeScale(n); // Speed up/slow down timeline by n seconds- use this for debugging to slow it down, or for faster playback in action
25 | tl.globalTimeScale(n); // changes all of the timescales at once- useful for multiple timelines.
26 | tl.progress(0.5); // Skip to halfway
27 | tl.resume(); // Continue playback
28 | tl.resume(n); // Continue playback at n seconds
29 | tl.play(n); // Play from n seconds- funnily, less useful
30 | tl.play(-n); // Play n seconds from end
31 |
32 | // also options, usage: new TimelineMax({example});
33 | paused:true // boolean, default is false
34 | yoyo:true // close to CSS animation direction: alternate- makes the timeline go back and forth.
35 | repeat:n // iteration count
36 | delay:n // delay of the whole timeline
37 | repeatDelay:n // delay in between iteration
38 |
39 | //-------------------------------------------------------
40 |
41 | // Transforms in GSAP
42 | // comments show CSS equivalent
43 |
44 | x: 100 // transform: translateX(100px)
45 | y: 100 // transform: translateY(100px)
46 | z: 100 // transform: translateZ(100px)
47 | // you do not need the null transform hack or hardware acceleration, it comes baked in with
48 | // force3d:true. If you want to unset this, force3d:false
49 | scale: 2 // transform: scale(2)
50 | scaleX: 2 // transform: scaleX(2)
51 | scaleY: 2 // transform: scaleY(2)
52 | scaleZ: 2 // transform: scaleZ(2)
53 | skew: 15 // transform: skew(15deg)
54 | skewX: 15 // transform: skewX(15deg)
55 | skewY: 15 // transform: skewY(15deg)
56 | skewZ: 15 // transform: skewZ(15deg)
57 | rotation: 180 // transform: rotate(180deg)
58 | rotationX: 180 // transform: rotateX(180deg)
59 | rotationY: 180 // transform: rotateY(180deg)
60 | rotationZ: 180 // transform: rotateZ(180deg)
61 | perspective: 1000 // transform: perspective(1000px)
62 | transformOrigin: '50% 50%' // transform-origin: 50% 50%
63 |
64 | //-------------------------------------------------------
65 |
66 | // DrawSVG
67 | // Use percentage, absolute values, or boolean
68 |
69 | drawSVG: true // full stroke drawn
70 | drawSVG:"100%" // same as true
71 | drawSVG: "10%" // the first 10%
72 | drawSVG: "10% 90%" // the first 10% to the last 90%
73 | drawSVG: "50% 50%" // looks like nothing because both ends at 50%
74 | drawSVG:"20 350" // the first 20px and 350px
75 |
76 | //-------------------------------------------------------
77 |
78 | // SplitText
79 | // creates an array (can use mySplitText.chars, in the case below, foo.chars), also wraps all in divs
80 |
81 | var foo = new SplitText("#bar", {
82 | type: "words, chars, lines",
83 | //optional- may animate better, may cause text reflow to be less natural
84 | position: "absolute",
85 | //optional
86 | wordsClass:"foobar"
87 | });
88 |
89 | // use in GSAP animation:
90 | tl.staggerFrom(mySplitText.chars, 0.8, {opacity:0, scaleX:0, ease:Power4.easeOut}, 0.05, "+=4");
91 |
92 | //-------------------------------------------------------
93 |
94 | // MorphSVG
95 |
96 | // You may need to convert the shape data to a path before you begin working
97 | MorphSVGPlugin.convertToPath("circle, rect, ellipse, line, polygon, polyline");
98 |
99 | TweenLite.to("#initial", 1, {morphSVG:"#next"}); //first shape tween
100 | TweenLite.to("#initial", 1, {morphSVG:"#second"}); //the second shape tween. Notice that we don't use #next, we continue to tween from the initial shape.
101 | TweenLite.to(“#polygon”, 2, {morphSVG:"240,220 240,70 70,70 70,220"}); //use a shape instead of an idea
102 | TweenLite.to(“#path”, 2, {morphSVG:"M10 315 L 110 215 A 30 50 0 0 1 162.55 162.45 L 172.55 152.45 A 30 50 -45 0 1 215.1 109.9 L 315 10"}); //use a path instead of an ID
103 |
104 | // Load shapeIndex to check out the optimal shape tween https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/findShapeIndex.js, it will start up a GUI
105 | // this plugin is smart and will use a repeat:-1 to continually show the tween so you can use the GUI.
106 |
107 | // use an existing ID
108 | findShapeIndex("#square", "#star");
109 | // or use path data
110 | findShapeIndex("#square", "M10 315 L 110 215 A 30 50 0 0 1 162.55 162.45 L 172.55 152.45 A 30 50 -45 0 1 215.1 109.9 L 315 10");
--------------------------------------------------------------------------------
/gsap-simple-tween/css/style.css:
--------------------------------------------------------------------------------
1 | div {
2 | width: 40px;
3 | height: 40px;
4 | background: teal;
5 | margin: 0 auto;
6 | transform: translate(0, 10px);
7 | }
--------------------------------------------------------------------------------
/gsap-simple-tween/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | A Pen by Sarah Drasner
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/gsap-simple-tween/js/index.js:
--------------------------------------------------------------------------------
1 | TweenLite.to("div", 2, {scaleY:0.75, scaleX:1.25, y:100, opacity:0.75, ease:Elastic.easeOut});
--------------------------------------------------------------------------------
/gsap-two-tweens/css/style.css:
--------------------------------------------------------------------------------
1 | .squares {
2 | float: left;
3 | width: 40px;
4 | height: 40px;
5 | margin-left: 10px;
6 | background: #3f717c;
7 | }
8 |
9 | .scene {
10 | margin: 20px auto;
11 | display: table;
12 | }
13 |
--------------------------------------------------------------------------------
/gsap-two-tweens/index.haml:
--------------------------------------------------------------------------------
1 | .scene
2 | -(1..12).each do |i|
3 | .squares
--------------------------------------------------------------------------------
/gsap-two-tweens/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | A Pen by Sarah Drasner
7 |
8 |
24 |
25 |
26 |
27 |
28 |