Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

♻️ Support img element #34028

Merged
merged 58 commits into from
May 20, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
486988e
Allow IMG so long as it contains a loading attribute
kristoferbaxter Apr 27, 2021
5bc0093
Make width and height mandatory
kristoferbaxter Apr 27, 2021
afe3dd5
Move method from within AMP.BaseElement to src/core/dom since it must…
kristoferbaxter Apr 27, 2021
3412e94
Update validator output
kristoferbaxter Apr 27, 2021
0627d01
Mid way stopping point on amp-lightbox-gallery cloneLightboxableElement_
kristoferbaxter Apr 27, 2021
ce7e2ae
Lightbox gallery now supports image elements
kristoferbaxter Apr 27, 2021
bf9daa8
Use Sets instead of objects
kristoferbaxter Apr 27, 2021
4d6a070
PR feedback on name of utility and usage of other utilities
kristoferbaxter Apr 27, 2021
7b12a26
Merge branch 'main' of github.com:ampproject/amphtml into support-img
kristoferbaxter Apr 28, 2021
c2aff18
lightbox had a bad reference
kristoferbaxter Apr 29, 2021
0259f76
Merge latest main
kristoferbaxter Apr 29, 2021
54d8fdd
Merge branch 'main' of github.com:ampproject/amphtml into support-img
kristoferbaxter Apr 29, 2021
538c254
Spy on the right thing now
kristoferbaxter Apr 29, 2021
9b7d7a7
Remove validator changes for a later step
kristoferbaxter Apr 29, 2021
0f732e2
Carriage returns at the end of the out files for the validator
kristoferbaxter Apr 29, 2021
c2bb89a
Updated carriage returns at end of files
kristoferbaxter Apr 29, 2021
04b82e5
Update comments
kristoferbaxter May 5, 2021
dffe889
Move auto lightbox to loadPromise
kristoferbaxter May 5, 2021
e174c68
Remove Set in amp-auto-lightbox
kristoferbaxter May 5, 2021
4592e36
Remove set in amp-image-lightbox
kristoferbaxter May 5, 2021
39006bd
Remove logic specific to amp-img for the img path
kristoferbaxter May 5, 2021
0d4eb49
Additional fixes for Set usage
kristoferbaxter May 6, 2021
59455c9
Merge branch 'main' of github.com:ampproject/amphtml into support-img
kristoferbaxter May 6, 2021
8937bdc
test for supporting img on amp-image-lightbox
kristoferbaxter May 6, 2021
90ff796
Add tests for propagate attributes helper and fix usage in iframe-video
kristoferbaxter May 6, 2021
a538b4c
Additional fixes for propagateAttributes from AMPElements
kristoferbaxter May 6, 2021
5705f71
Merge branch 'main' of github.com:ampproject/amphtml into support-img
kristoferbaxter May 6, 2021
20127b1
Merge conflicts
kristoferbaxter May 11, 2021
c2d3bf5
Bad merge
kristoferbaxter May 11, 2021
0b63e1a
pass ampdoc in Criteria so its not obtained from an HTMLElement
kristoferbaxter May 11, 2021
519fa81
change order of arguments to reduce test rewriting for Criteria
kristoferbaxter May 11, 2021
1130dd1
Fix types in propagate attributes
kristoferbaxter May 12, 2021
d31c757
Prettier formatting
kristoferbaxter May 12, 2021
fade270
Merge branch 'main' of github.com:ampproject/amphtml into support-img
kristoferbaxter May 12, 2021
c4292e8
different prettier versions
kristoferbaxter May 12, 2021
bb8a2d9
Merge branch 'main' of github.com:ampproject/amphtml into support-img
kristoferbaxter May 13, 2021
21a8dc2
update core utility with improved typing from below
kristoferbaxter May 13, 2021
15c5b58
Address part of Alans feedback
kristoferbaxter May 13, 2021
acbfd3a
types
kristoferbaxter May 13, 2021
8f7fbfc
remove toLowerCase on tagName
kristoferbaxter May 13, 2021
b448094
Test changes from PR feedback (and applied elsewhere in the file)
kristoferbaxter May 13, 2021
7e8d6a9
Add support for native HTMLImageElements to amp-image-slider
kristoferbaxter May 14, 2021
f90f409
Add test using HTMLImageElements
kristoferbaxter May 14, 2021
643aef7
Revert changes to gestures for a later PR
kristoferbaxter May 14, 2021
1cdc0ec
Continued progress, pan zoom works and lightbox gallery is underway
kristoferbaxter May 18, 2021
a46e1ee
Merge in latest main
kristoferbaxter May 18, 2021
222d247
LayoutScheduled for amp-carousel 0.1 when unlayout happening
kristoferbaxter May 19, 2021
a6885da
Remove image support for amp-image-viewer for a future PR
kristoferbaxter May 19, 2021
1f6492d
Image Viewer no longer needs to exclude itself from using loadPromise…
kristoferbaxter May 19, 2021
83a0a07
Remove console logging for carousel debugging:
kristoferbaxter May 19, 2021
02fbc8c
Remove breaks in parsing of children for amp-image-slider
kristoferbaxter May 19, 2021
05d95d5
No need to provide an empty array for the Set constructor
kristoferbaxter May 19, 2021
033f3aa
Remaining console
kristoferbaxter May 19, 2021
d8f19fe
Nit
kristoferbaxter May 19, 2021
d8fd326
Remove more intermediary state changes
kristoferbaxter May 19, 2021
343024c
Naming nit
kristoferbaxter May 20, 2021
ccb6010
Merge conflicts
kristoferbaxter May 20, 2021
61c93db
prettier formatting in test
kristoferbaxter May 20, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions builtins/amp-img.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {Services} from '../src/services';
import {dev} from '../src/log';
import {guaranteeSrcForSrcsetUnsupportedBrowsers} from '../src/utils/img';
import {listen} from '../src/event-helper';
import {propagateAttributes} from '../src/core/dom/propagateAttributes';
import {propagateObjectFitStyles, setImportantStyles} from '../src/style';
import {registerElement} from '../src/service/custom-element-registry';
import {removeElement, scopedQuerySelector} from '../src/dom';
Expand Down Expand Up @@ -130,8 +131,9 @@ export class AmpImg extends BaseElement {
this.element
);
}
this.propagateAttributes(
propagateAttributes(
attrs,
this.element,
this.img_,
/* opt_removeMissingAttrs */ true
);
Expand Down Expand Up @@ -216,7 +218,7 @@ export class AmpImg extends BaseElement {

// It is important to call this before setting `srcset` attribute.
this.maybeGenerateSizes_(/* sync setAttribute */ true);
this.propagateAttributes(ATTRIBUTES_TO_PROPAGATE, this.img_);
propagateAttributes(ATTRIBUTES_TO_PROPAGATE, this.element, this.img_);
this.propagateDataset(this.img_);
if (!IS_ESM) {
guaranteeSrcForSrcsetUnsupportedBrowsers(this.img_);
Expand Down
1 change: 1 addition & 0 deletions examples/amp-lightbox.amp.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ <h2>Scrollable Lightbox</h2>
Lorem ipsum dolor sit amet, has nisl nihil convenire et, vim at aeque inermis reprehendunt. Propriae tincidunt id nec, elit nusquam te mea, ius noster platonem in. Mea an idque minim, sit sale deleniti apeirian et. Omnium legendos tractatos cu mea. Vix in stet dolorem accusamus. Iisque rationibus consetetur in cum, quo unum nulla legere ut. Simul numquam saperet no sit.
</p>
<p>
<img src="https://lh3.googleusercontent.com/5rcQ32ml8E5ONp9f9-Rf78IofLb9QjS5_0mqsY1zEFc=w300-h200-no" width=300 height=200 loading="lazy" />
<amp-img src="https://lh3.googleusercontent.com/5rcQ32ml8E5ONp9f9-Rf78IofLb9QjS5_0mqsY1zEFc=w300-h200-no" width=300 height=200></amp-img>
</p>
<p class="lightbox-text">
Expand Down
7 changes: 7 additions & 0 deletions examples/image-lightbox.amp.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@
<script async src="https://cdn.ampproject.org/v0.js"></script>
</head>
<body>
<!-- Standard Image Element -->
<img on="tap:ampimagelightbox"
role="button"
src="img/bigbuckbunny.jpg"
width="500"
height="500"
loading="lazy" />
<!--
- Test layout: intrinsic, fill, fixed, fixed-height, responsive
-->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
PASS
FAIL
| <!--
| Copyright 2017 The AMP HTML Authors. All Rights Reserved.
|
Expand Down Expand Up @@ -50,6 +50,12 @@ PASS
| <p amp-access="lemur"></p>
| <noscript>
| <img src="https://example.com/" amp-access="lemur" />
>> ^~~~~~~~~
amp-access-laterpay/0.1/test/validator-amp-access-laterpay.html:51:2 The mandatory attribute 'height' is missing in tag 'img'.
>> ^~~~~~~~~
amp-access-laterpay/0.1/test/validator-amp-access-laterpay.html:51:2 The mandatory attribute 'loading' is missing in tag 'img'.
>> ^~~~~~~~~
amp-access-laterpay/0.1/test/validator-amp-access-laterpay.html:51:2 The mandatory attribute 'width' is missing in tag 'img'.
| </noscript>
|
| <!-- test some other attributes -->
Expand All @@ -67,4 +73,4 @@ PASS
| </div>
|
| </body>
| </html>
| </html>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
PASS
FAIL
| <!--
| Copyright 2018 The AMP HTML Authors. All Rights Reserved.
|
Expand Down Expand Up @@ -49,6 +49,12 @@ PASS
| <p amp-access="lemur"></p>
| <noscript>
| <img src="https://example.com/" amp-access="lemur" />
>> ^~~~~~~~~
amp-access-laterpay/0.2/test/validator-amp-access-laterpay.html:50:2 The mandatory attribute 'height' is missing in tag 'img'.
>> ^~~~~~~~~
amp-access-laterpay/0.2/test/validator-amp-access-laterpay.html:50:2 The mandatory attribute 'loading' is missing in tag 'img'.
>> ^~~~~~~~~
amp-access-laterpay/0.2/test/validator-amp-access-laterpay.html:50:2 The mandatory attribute 'width' is missing in tag 'img'.
| </noscript>
| <!-- test some other attributes -->
| <div amp-access="lemur"
Expand All @@ -64,4 +70,4 @@ PASS
| amp-access-id="lemur">
| </div>
| </body>
| </html>
| </html>
10 changes: 8 additions & 2 deletions extensions/amp-access/0.1/test/validator-amp-access.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
PASS
FAIL
| <!--
| Copyright 2015 The AMP HTML Authors. All Rights Reserved.
|
Expand Down Expand Up @@ -47,6 +47,12 @@ PASS
| <p amp-access="lemur"></p>
| <noscript>
| <img src="https://example.com/" amp-access="lemur" />
>> ^~~~~~~~~
amp-access/0.1/test/validator-amp-access.html:48:2 The mandatory attribute 'height' is missing in tag 'img'.
>> ^~~~~~~~~
amp-access/0.1/test/validator-amp-access.html:48:2 The mandatory attribute 'loading' is missing in tag 'img'.
>> ^~~~~~~~~
amp-access/0.1/test/validator-amp-access.html:48:2 The mandatory attribute 'width' is missing in tag 'img'.
| </noscript>
|
| <!-- test some other attributes -->
Expand All @@ -64,4 +70,4 @@ PASS
| </span>
|
| </body>
| </html>
| </html>
6 changes: 4 additions & 2 deletions extensions/amp-anim/0.1/amp-anim.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
observeWithSharedInOb,
unobserveWithSharedInOb,
} from '../../../src/viewport-observer';
import {propagateAttributes} from '../../../src/core/dom/propagateAttributes';
import {propagateObjectFitStyles} from '../../../src/style';

const TAG = 'amp-anim';
Expand Down Expand Up @@ -55,7 +56,7 @@ export class AmpAnim extends AMP.BaseElement {
buildCallback() {
this.img_ = new Image();
this.img_.setAttribute('decoding', 'async');
this.propagateAttributes(BUILD_ATTRIBUTES, this.img_);
propagateAttributes(BUILD_ATTRIBUTES, this.element, this.img_);
this.applyFillContent(this.img_, true);
propagateObjectFitStyles(this.element, this.img_);

Expand Down Expand Up @@ -88,8 +89,9 @@ export class AmpAnim extends AMP.BaseElement {
const img = dev().assertElement(this.img_);
// Remove missing attributes to remove the placeholder srcset if none is
// specified on the element.
this.propagateAttributes(
propagateAttributes(
LAYOUT_ATTRIBUTES,
this.element,
img,
/* opt_removeMissingAttrs */ true
);
Expand Down
10 changes: 8 additions & 2 deletions extensions/amp-audio/0.1/amp-audio.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {closestAncestorElementBySelector} from '../../../src/dom';
import {dev, user} from '../../../src/log';
import {getMode} from '../../../src/mode';
import {listen} from '../../../src/event-helper';
import {propagateAttributes} from '../../../src/core/dom/propagateAttributes';
import {setIsMediaComponent} from '../../../src/video-interface';
import {triggerAnalyticsEvent} from '../../../src/analytics';

Expand Down Expand Up @@ -85,7 +86,11 @@ export class AmpAudio extends AMP.BaseElement {
if (src !== undefined) {
assertHttpsUrl(src, this.element);
}
this.propagateAttributes(['src', 'loop', 'controlsList'], this.audio_);
propagateAttributes(
['src', 'loop', 'controlsList'],
this.element,
this.audio_
);
}

const artist = mutations['artist'];
Expand Down Expand Up @@ -119,7 +124,7 @@ export class AmpAudio extends AMP.BaseElement {
if (src) {
assertHttpsUrl(src, this.element);
}
this.propagateAttributes(
propagateAttributes(
[
'src',
'preload',
Expand All @@ -131,6 +136,7 @@ export class AmpAudio extends AMP.BaseElement {
'aria-labelledby',
'controlsList',
],
this.element,
audio
);

Expand Down
3 changes: 2 additions & 1 deletion extensions/amp-brid-player/0.1/amp-brid-player.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {getData, listen} from '../../../src/event-helper';
import {htmlFor} from '../../../src/static-template';
import {installVideoManagerForDoc} from '../../../src/service/video-manager-impl';
import {isLayoutSizeDefined} from '../../../src/layout';
import {propagateAttributes} from '../../../src/core/dom/propagateAttributes';

const TAG = 'amp-brid-player';

Expand Down Expand Up @@ -249,7 +250,7 @@ class AmpBridPlayer extends AMP.BaseElement {
</amp-img>
</amp-img>`;

this.propagateAttributes(['aria-label'], placeholder);
propagateAttributes(['aria-label'], this.element, placeholder);
this.applyFillContent(placeholder);

placeholder.setAttribute(
Expand Down
3 changes: 2 additions & 1 deletion extensions/amp-gfycat/0.1/amp-gfycat.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
import {getData, listen} from '../../../src/event-helper';
import {installVideoManagerForDoc} from '../../../src/service/video-manager-impl';
import {isLayoutSizeDefined} from '../../../src/layout';
import {propagateAttributes} from '../../../src/core/dom/propagateAttributes';

const TAG = 'amp-gfycat';

Expand Down Expand Up @@ -89,7 +90,7 @@ class AmpGfycat extends AMP.BaseElement {
createPlaceholderCallback() {
const placeholder = this.win.document.createElement('amp-img');
const videoid = dev().assertString(this.videoid_);
this.propagateAttributes(['alt', 'aria-label'], placeholder);
propagateAttributes(['alt', 'aria-label'], this.element, placeholder);
placeholder.setAttribute(
'src',
'https://thumbs.gfycat.com/' + encodeURIComponent(videoid) + '-poster.jpg'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {Services} from '../../../src/services';
import {addParamToUrl} from '../../../src/url';
import {dev, userAssert} from '../../../src/log';
import {isLayoutSizeDefined} from '../../../src/layout';
import {propagateAttributes} from '../../../src/core/dom/propagateAttributes';
import {removeElement} from '../../../src/dom';

export const TAG = 'amp-google-document-embed';
Expand Down Expand Up @@ -90,7 +91,7 @@ export class AmpDriveViewer extends AMP.BaseElement {

iframe.setAttribute('frameborder', '0');
iframe.setAttribute('allowfullscreen', '');
this.propagateAttributes(ATTRIBUTES_TO_PROPAGATE, iframe);
propagateAttributes(ATTRIBUTES_TO_PROPAGATE, this.element, iframe);

iframe.src = this.getSrc_(this.element.getAttribute('src'));

Expand All @@ -105,7 +106,12 @@ export class AmpDriveViewer extends AMP.BaseElement {
(value) => mutations[value] !== undefined
);
const iframe = dev().assertElement(this.iframe_);
this.propagateAttributes(attrs, iframe, /* opt_removeMissingAttrs */ true);
propagateAttributes(
attrs,
this.element,
iframe,
/* opt_removeMissingAttrs */ true
);
const src = mutations['src'];
if (src) {
iframe.src = this.getSrc_(src);
Expand Down
5 changes: 3 additions & 2 deletions extensions/amp-iframe/0.1/amp-iframe.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {isAdPositionAllowed} from '../../../src/ad-helper';
import {isExperimentOn} from '../../../src/experiments';
import {moveLayoutRect} from '../../../src/layout-rect';
import {parseJson} from '../../../src/json';
import {propagateAttributes} from '../../../src/core/dom/propagateAttributes';
import {removeElement} from '../../../src/dom';
import {removeFragment} from '../../../src/url';
import {setStyle} from '../../../src/style';
Expand Down Expand Up @@ -414,7 +415,7 @@ export class AmpIframe extends AMP.BaseElement {
setStyle(iframe, 'zIndex', -1);
}

this.propagateAttributes(ATTRIBUTES_TO_PROPAGATE, iframe);
propagateAttributes(ATTRIBUTES_TO_PROPAGATE, this.element, iframe);

// TEMPORARY: disable `allow=autoplay`
// This is a workaround for M72-M74 user-activation breakage.
Expand Down Expand Up @@ -619,7 +620,7 @@ export class AmpIframe extends AMP.BaseElement {
}
if (this.iframe_ && mutations['title']) {
// only propagating title because propagating all causes e2e error:
this.propagateAttributes(['title'], this.iframe_);
propagateAttributes(['title'], this.element, this.iframe_);
}
}

Expand Down
22 changes: 18 additions & 4 deletions extensions/amp-image-lightbox/0.1/amp-image-lightbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
layoutRectLtwh,
moveLayoutRect,
} from '../../../src/layout-rect';
import {propagateAttributes} from '../../../src/core/dom/propagateAttributes';
import {setStyles, toggle} from '../../../src/style';
import {srcsetFromElement} from '../../../src/srcset';

Expand All @@ -49,6 +50,11 @@ const SUPPORTED_ELEMENTS_ = {
'amp-img': true,
'amp-anim': true,
};
/** @private @const {!Object<string, boolean>} */
const SUPPORTED_NATIVE_ELEMENTS = {
'img': true,
'picture': true,
};

/** @private @const */
const ARIA_ATTRIBUTES = ['aria-label', 'aria-describedby', 'aria-labelledby'];
Expand Down Expand Up @@ -253,9 +259,15 @@ export class ImageViewer {
this.setSourceDimensions_(sourceElement, sourceImage);
this.srcset_ = srcsetFromElement(sourceElement);

sourceElement.getImpl().then((elem) => {
elem.propagateAttributes(ARIA_ATTRIBUTES, this.image_);
});
if (SUPPORTED_NATIVE_ELEMENTS[sourceElement.tagName.toLowerCase()]) {
propagateAttributes(ARIA_ATTRIBUTES, sourceElement, this.image_);
} else {
sourceElement
.getImpl()
.then((elem) =>
propagateAttributes(ARIA_ATTRIBUTES, elem, this.image_)
);
}

if (sourceImage && isLoaded(sourceImage) && sourceImage.src) {
// Set src provisionally to the known loaded value for fast display.
Expand Down Expand Up @@ -871,8 +883,10 @@ class AmpImageLightbox extends AMP.BaseElement {
this.buildLightbox_();

const source = invocation.caller;
const tagName = source.tagName.toLowerCase();
userAssert(
source && SUPPORTED_ELEMENTS_[source.tagName.toLowerCase()],
source &&
(SUPPORTED_ELEMENTS_[tagName] || SUPPORTED_NATIVE_ELEMENTS[tagName]),
'Unsupported element: %s',
source.tagName
);
Expand Down
3 changes: 2 additions & 1 deletion extensions/amp-image-viewer/0.1/amp-image-viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
observeContentSize,
unobserveContentSize,
} from '../../../src/utils/size-observer';
import {propagateAttributes} from '../../../src/core/dom/propagateAttributes';
import {setStyles} from '../../../src/style';
import {srcsetFromElement} from '../../../src/srcset';

Expand Down Expand Up @@ -321,7 +322,7 @@ export class AmpImageViewer extends AMP.BaseElement {
st.toggle(ampImg, false);
this.element.appendChild(this.image_);
return ampImg.getImpl().then((ampImg) => {
ampImg.propagateAttributes(ARIA_ATTRIBUTES, this.image_);
propagateAttributes(ARIA_ATTRIBUTES, ampImg, this.image_);
});
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {dict} from '../../../src/core/types/object';
import {htmlFor} from '../../../src/static-template';
import {isLayoutSizeDefined} from '../../../src/layout';
import {matches, scopedQuerySelector} from '../../../src/dom';
import {propagateAttributes} from '../../../src/core/dom/propagateAttributes';
import {setStyle} from '../../../src/style';

/**
Expand Down Expand Up @@ -246,7 +247,7 @@ export class AmpInlineGalleryThumbnails extends AMP.BaseElement {

// We create with loop defaulting to false above, and allow it to be
// overwriten.
this.propagateAttributes(['loop'], this.carousel_);
propagateAttributes(['loop'], this.element, this.carousel_);
this.element.appendChild(this.carousel_);
}
}
3 changes: 2 additions & 1 deletion extensions/amp-jwplayer/0.1/amp-jwplayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {getMode} from '../../../src/mode';
import {installVideoManagerForDoc} from '../../../src/service/video-manager-impl';
import {isLayoutSizeDefined} from '../../../src/layout';
import {once} from '../../../src/core/types/function';
import {propagateAttributes} from '../../../src/core/dom/propagateAttributes';

const JWPLAYER_EVENTS = {
'ready': VideoEvents.LOAD,
Expand Down Expand Up @@ -349,7 +350,7 @@ class AmpJWPlayer extends AMP.BaseElement {
return;
}
const placeholder = this.win.document.createElement('amp-img');
this.propagateAttributes(['aria-label'], placeholder);
propagateAttributes(['aria-label'], this.element, placeholder);
placeholder.setAttribute(
'src',
'https://content.jwplatform.com/thumbs/' +
Expand Down
Loading