Bug 1918332 [wpt PR 48116] - [@container] Move scroll-state() query tests to wpt, a=testonly

Automatic update from web-platform-tests
[@container] Move scroll-state() query tests to wpt

The spec PR landed, so tests should now be supported by the spec.

The 'inset-' prefix for the stuck keywords per were not per spec and
remove both from tests and implementation.

The tests for the 'none'-keyword for snapped and stuck does not match
the current spec draft, but will be fixed by [1]

[1] https://github.com/w3c/csswg-drafts/pull/10874/

Bug: 40279568, 40268059
Change-Id: I7cf8d3a2f251eaea0c3a78329c46a7bfddd85dda
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5850484
Reviewed-by: Anders Hartvoll Ruud <andruud@chromium.org>
Commit-Queue: Rune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1354415}

--

wpt-commits: d2c522e29aa466bbfcc863b51aa3556aff5a248c
wpt-pr: 48116
This commit is contained in:
Rune Lillesveen 2024-09-15 22:08:58 +00:00 committed by moz-wptsync-bot
parent f9b46ad39c
commit b9ecd4c12b
13 changed files with 653 additions and 0 deletions

View File

@ -0,0 +1,80 @@
<!DOCTYPE html>
<title>CSS Conditional Test: @container snapped query parsing</title>
<link rel="help" href="https://drafts.csswg.org/css-conditional-5/#container-rule">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/css-conditional/container-queries/support/cq-testcommon.js"></script>
<div style="container-name:name;container-type:scroll-state">
<main id="main"></main>
</div>
<script>
setup(() => assert_implements_container_queries());
function cleanup_main() {
while (main.firstChild)
main.firstChild.remove();
}
function set_style(text) {
let style = document.createElement('style');
style.innerText = text;
main.append(style);
return style;
}
function test_rule_valid(query) {
test(t => {
t.add_cleanup(cleanup_main);
let style = set_style(`@container ${query} {}`);
assert_equals(style.sheet.rules.length, 1);
}, query);
}
function test_condition_invalid(condition) {
test(t => {
t.add_cleanup(cleanup_main);
let style = set_style(`@container name ${condition} {}`);
assert_equals(style.sheet.rules.length, 0);
}, condition);
}
// Tests that 1) the condition parses, and 2) is either "unknown" or not, as
// specified.
function test_condition_valid(condition, unknown) {
test(t => {
t.add_cleanup(cleanup_main);
let style = set_style(`
@container name ${condition} {}
@container name (${condition}) or (not (${condition})) { main { --match:true; } }
`);
assert_equals(style.sheet.rules.length, 2);
const expected = unknown ? '' : 'true';
assert_equals(getComputedStyle(main).getPropertyValue('--match'), expected);
}, condition);
}
function test_condition_known(condition) {
test_condition_valid(condition, false /* unknown */);
}
function test_condition_unknown(condition) {
test_condition_valid(condition, true /* unknown */);
}
test_condition_known('scroll-state(snapped)');
test_condition_known('scroll-state(snapped: x)');
test_condition_known('scroll-state(snapped: y)');
test_condition_known('scroll-state(snapped: none)');
test_condition_known('scroll-state(snapped: block)');
test_condition_known('scroll-state(snapped: inline)');
test_condition_known('(scroll-state(snapped: block))');
test_condition_known('scroll-state((snapped: inline))');
test_condition_known('scroll-state(not ((snapped: inline) and (snapped: block)))');
test_condition_known('scroll-state((snapped: x) or (snapped: y))');
test_condition_unknown('scroll-state(snapped: auto)');
test_condition_unknown('scroll-state(snapped: true)');
test_condition_unknown('scroll-state(style(snapped: inline))');
test_condition_unknown('style(scroll-state(snapped: inline))');
test_condition_unknown('scroll-state(snapped:)');
</script>

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<title>CSS Container Queries: scroll-state(snapped) conditionText serialization</title>
<link rel="help" href="https://drafts.csswg.org/css-conditional-5/#container-queries">
<link rel="help" href="https://drafts.csswg.org/cssom/#serialize-a-css-rule">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/css-conditional/container-queries/support/cq-testcommon.js"></script>
<style id="testSheet">
@container scroll-state( snapped:inline) { }
@container scroll-STate(snapped: ) { }
@container scroll-STate(snapped) { }
@container scroll-state( ( snapped: INLINE) OR ( SNAPPED: BlOCK ) ) { }
@container scroll-state (snapped: inline) { }
</style>
<script>
setup(() => {
assert_implements_container_queries();
assert_equals(testSheet.sheet.cssRules.length, 5);
});
const tests = [
["scroll-state(snapped: inline)", "Normalize spaces"],
["scroll-STate(snapped: )", "No value - invalid, serializes as <general-enclosed>"],
["scroll-state(snapped)", "Boolean context"],
["scroll-state((snapped: inline) or (snapped: block))", "Logical with 'or'"],
["scroll-state (snapped: inline)", "Not a scroll-state function with space before '('"]
].map((e, i) => [testSheet.sheet.cssRules[i], ...e]);
tests.forEach((t) => {
test(() => assert_equals(t[0].conditionText, t[1]), t[2]);
});
</script>

View File

@ -0,0 +1,85 @@
<!DOCTYPE html>
<title>CSS Conditional Test: @container stuck query parsing</title>
<link rel="help" href="https://drafts.csswg.org/css-conditional-5/#container-rule">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/css-conditional/container-queries/support/cq-testcommon.js"></script>
<div style="container-name:name;container-type:scroll-state">
<main id="main"></main>
</div>
<script>
setup(() => assert_implements_container_queries());
function cleanup_main() {
while (main.firstChild)
main.firstChild.remove();
}
function set_style(text) {
let style = document.createElement('style');
style.innerText = text;
main.append(style);
return style;
}
function test_rule_valid(query) {
test(t => {
t.add_cleanup(cleanup_main);
let style = set_style(`@container ${query} {}`);
assert_equals(style.sheet.rules.length, 1);
}, query);
}
function test_condition_invalid(condition) {
test(t => {
t.add_cleanup(cleanup_main);
let style = set_style(`@container name ${condition} {}`);
assert_equals(style.sheet.rules.length, 0);
}, condition);
}
// Tests that 1) the condition parses, and 2) is either "unknown" or not, as
// specified.
function test_condition_valid(condition, unknown) {
test(t => {
t.add_cleanup(cleanup_main);
let style = set_style(`
@container name ${condition} {}
@container name (${condition}) or (not (${condition})) { main { --match:true; } }
`);
assert_equals(style.sheet.rules.length, 2);
const expected = unknown ? '' : 'true';
assert_equals(getComputedStyle(main).getPropertyValue('--match'), expected);
}, condition);
}
function test_condition_known(condition) {
test_condition_valid(condition, false /* unknown */);
}
function test_condition_unknown(condition) {
test_condition_valid(condition, true /* unknown */);
}
test_condition_known('scroll-state(stuck)');
test_condition_known('scroll-state(stuck: none)');
test_condition_known('scroll-state(stuck: top)');
test_condition_known('scroll-state(stuck: left)');
test_condition_known('scroll-state(stuck: bottom)');
test_condition_known('scroll-state(stuck: right)');
test_condition_known('scroll-state(stuck: block-start)');
test_condition_known('scroll-state(stuck: block-end)');
test_condition_known('scroll-state(stuck: inline-start)');
test_condition_known('scroll-state(stuck: inline-end)');
test_condition_known('(scroll-state(stuck: top))');
test_condition_known('scroll-state((stuck: top))');
test_condition_known('scroll-state(not ((stuck: top) and (stuck: bottom)))');
test_condition_known('scroll-state((stuck: right) or (stuck: left))');
test_condition_unknown('scroll-state(stuck: auto)');
test_condition_unknown('scroll-state(stuck: true)');
test_condition_unknown('scroll-state(style(stuck: top))');
test_condition_unknown('style(scroll-state(stuck: top))');
test_condition_unknown('scroll-state(stuck:)');
test_condition_unknown('scroll-state(--foo)');
</script>

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<title>CSS Container Queries: scroll-state(stuck) conditionText serialization</title>
<link rel="help" href="https://drafts.csswg.org/css-conditional-5/#container-queries">
<link rel="help" href="https://drafts.csswg.org/cssom/#serialize-a-css-rule">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/css-conditional/container-queries/support/cq-testcommon.js"></script>
<style id="testSheet">
@container scroll-state( stuck:top) { }
@container scroll-STate(stuck: ) { }
@container scroll-STate(stuck) { }
@container scroll-state( ( stuck: BOTTOM) OR ( STUCK: inline-START ) ) { }
@container scroll-state (stuck: top) { }
</style>
<script>
setup(() => {
assert_implements_container_queries();
assert_equals(testSheet.sheet.cssRules.length, 5);
});
const tests = [
["scroll-state(stuck: top)", "Normalize spaces"],
["scroll-STate(stuck: )", "No value - invalid, serializes as <general-enclosed>"],
["scroll-state(stuck)", "Boolean context"],
["scroll-state((stuck: bottom) or (stuck: inline-start))", "Logical with 'or'"],
["scroll-state (stuck: top)", "Not a scroll-state function with space before '('"]
].map((e, i) => [testSheet.sheet.cssRules[i], ...e]);
tests.forEach((t) => {
test(() => assert_equals(t[0].conditionText, t[1]), t[2]);
});
</script>

View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Conditional Test: Computed values of container-type with scroll-state</title>
<link rel="help" href="https://drafts.csswg.org/css-conditional-5/#container-type">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/computed-testcommon.js"></script>
<script src="/css/css-conditional/container-queries/support/cq-testcommon.js"></script>
<div id="target"></div>
<script>
setup(() => assert_implements_container_queries());
test_computed_value('container-type', 'scroll-state');
test_computed_value('container-type', 'scroll-state size', 'size scroll-state');
test_computed_value('container-type', 'inline-size scroll-state');
</script>

View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<title>CSS Container Queries: container type scroll-state</title>
<link rel="help" href="https://drafts.csswg.org/css-conditional-5/#container-type">
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/6402#issuecomment-1812973013">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/css-conditional/container-queries/support/cq-testcommon.js"></script>
<style>
body { margin: 0; }
#container {
margin-top: 100px;
container-type: scroll-state;
width: fit-content;
}
#child {
margin-top: 100px;
width: 100px;
height: 100px;
}
</style>
<div id="container">
<div id="child">Non-zero-content</div>
</div>
<script>
test(() => {
assert_equals(container.offsetWidth, 100, "Intrinsically sized width");
assert_equals(container.offsetHeight, 100, "Intrinsically sized height");
}, "container-type:scroll-state does not apply size containment");
test(() => {
assert_equals(child.getBoundingClientRect().y, 100,
"#container and #child margins collapsed");
}, "container-type:scroll-state does not establish a formatting context");
</script>

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Conditional Test: Parsing of container-type with scroll-state</title>
<link rel="help" href="https://drafts.csswg.org/css-conditional-5/#container-type">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
<script src="/css/css-conditional/container-queries/support/cq-testcommon.js"></script>
<div id="target"></div>
<script>
setup(() => assert_implements_container_queries());
test_valid_value('container-type', 'scroll-state');
test_valid_value('container-type', 'size scroll-state');
test_valid_value('container-type', 'scroll-state inline-size', 'inline-size scroll-state');
test_invalid_value('container-type', 'scroll-state scroll-state');
test_invalid_value('container-type', 'normal scroll-state');
test_invalid_value('container-type', 'inline-size block-size scroll-state');
</script>

View File

@ -0,0 +1,47 @@
<!DOCTYPE html>
<title>@container: scroll-state(snapped) matching for initial rendering</title>
<link rel="help" href="https://drafts.csswg.org/css-conditional-5/#container-rule">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/css-conditional/container-queries/support/cq-testcommon.js"></script>
<script src="/css/css-transitions/support/helper.js"></script>
<style>
:root {
scroll-snap-type: block mandatory;
}
body {
margin: 0;
}
#filler-before {
height: 200px;
}
#filler-after {
height: 10000px;
}
#snapped {
container-name: initially-snapped;
container-type: scroll-state;
scroll-snap-align: start;
width: 100px;
height: 100px;
background: teal;
}
@container initially-snapped scroll-state(snapped: block) {
span { --snapped: yes }
}
</style>
<div id="filler-before"></div>
<div id="snapped">
<span id="target">My container is snapped</span>
</div>
<div id="filler-after"></div>
<script>
setup(() => assert_implements_container_queries());
promise_test(async t => {
await waitForAnimationFrames(2);
assert_equals(getComputedStyle(target).getPropertyValue("--snapped"), "yes");
assert_equals(document.documentElement.scrollTop, 200);
}, "Check that scroll-state(snapped: block) matches");
</script>

View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<title>@container: scroll-state(stuck) matching for initial rendering</title>
<link rel="help" href="https://drafts.csswg.org/css-conditional-5/#container-rule">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/css-conditional/container-queries/support/cq-testcommon.js"></script>
<script src="/css/css-transitions/support/helper.js"></script>
<style>
#filler {
height: 10000px;
}
#stuck {
container-name: initially-stuck;
container-type: scroll-state;
position: sticky;
bottom: 0;
}
@container initially-stuck scroll-state(stuck: bottom) {
span { --stuck: yes }
}
</style>
<div id="filler"></div>
<div id="stuck">
<span id="target">My container is stuck</span>
</div>
<script>
setup(() => assert_implements_container_queries());
promise_test(async t => {
await waitForAnimationFrames(2);
assert_equals(getComputedStyle(target).getPropertyValue("--stuck"), "yes");
}, "Check that scroll-state(stuck: bottom) matches");
</script>

View File

@ -0,0 +1,55 @@
<!DOCTYPE html>
<title>@container: scroll-state(snapped) changed after scroll</title>
<link rel="help" href="https://drafts.csswg.org/css-conditional-5/#container-rule">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/css-conditional/container-queries/support/cq-testcommon.js"></script>
<script src="/css/css-transitions/support/helper.js"></script>
<style>
:root {
scroll-snap-type: block mandatory;
}
body {
margin: 0;
}
#filler {
height: 10000px;
}
.snapped {
container-type: scroll-state;
scroll-snap-align: start;
width: 100px;
height: 100px;
margin-bottom: 100px;
background: teal;
}
@container scroll-state(snapped: block) {
span { --snapped: yes }
}
</style>
<div class="snapped">
<span id="target1"></span>
</div>
<div class="snapped">
<span id="target2"></span>
</div>
<div id="filler"></div>
<script>
setup(() => assert_implements_container_queries());
promise_test(async t => {
await waitForAnimationFrames(2);
assert_equals(getComputedStyle(target1).getPropertyValue("--snapped"), "yes");
assert_equals(getComputedStyle(target2).getPropertyValue("--snapped"), "");
assert_equals(document.documentElement.scrollTop, 0);
}, "Check that scroll-state(snapped: block) matches on #target1 before scroll");
promise_test(async t => {
document.documentElement.scrollTop = 300;
await waitForAnimationFrames(2);
assert_equals(getComputedStyle(target1).getPropertyValue("--snapped"), "");
assert_equals(getComputedStyle(target2).getPropertyValue("--snapped"), "yes");
assert_equals(document.documentElement.scrollTop, 200);
}, "Check that scroll-state(snapped: block) matches on #target2 after scroll");
</script>

View File

@ -0,0 +1,84 @@
<!DOCTYPE html>
<title>@container: scroll-state(snapped) property changes</title>
<link rel="help" href="https://drafts.csswg.org/css-conditional-5/#container-rule">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/css-conditional/container-queries/support/cq-testcommon.js"></script>
<script src="/css/css-transitions/support/helper.js"></script>
<style>
:root {
scroll-snap-type: block mandatory;
}
body {
margin: 0;
}
#filler-before {
height: 200px;
}
#filler-after {
height: 10000px;
}
#snapped {
container-type: scroll-state;
scroll-snap-align: start;
width: 100px;
height: 100px;
background: teal;
}
span {
--snapped: no;
@container scroll-state(snapped) {
--snapped: yes;
}
}
</style>
<div id="filler-before"></div>
<div id="snapped">
<span id="target">My container is snapped</span>
</div>
<div id="filler-after"></div>
<script>
setup(() => assert_implements_container_queries());
promise_test(async t => {
await waitForAnimationFrames(2);
assert_equals(getComputedStyle(target).getPropertyValue("--snapped"), "yes");
}, "Check scroll-state(snapped) initially matching");
promise_test(async t => {
t.add_cleanup(async () => snapped.style.scrollSnapAlign = "");
snapped.style.scrollSnapAlign = "initial";
await waitForAnimationFrames(2);
assert_equals(getComputedStyle(target).getPropertyValue("--snapped"), "no",
"scroll-snap-align removed");
snapped.style.scrollSnapAlign = "";
await waitForAnimationFrames(2);
assert_equals(getComputedStyle(target).getPropertyValue("--snapped"), "yes",
"scroll-snap-align re-added");
}, "Check scroll-state(snapped) not matching when scroll-snap-align is removed");
promise_test(async t => {
t.add_cleanup(async () => document.documentElement.style.scrollSnapType = "");
document.documentElement.style.scrollSnapType = "initial";
await waitForAnimationFrames(2);
assert_equals(getComputedStyle(target).getPropertyValue("--snapped"), "no",
"scroll-snap-type removed");
document.documentElement.style.scrollSnapType = "";
await waitForAnimationFrames(2);
assert_equals(getComputedStyle(target).getPropertyValue("--snapped"), "yes",
"scroll-snap-type re-added");
}, "Check scroll-state(snapped) not matching when scroll-snap-type is removed");
promise_test(async t => {
t.add_cleanup(async () => snapped.style.containerType = "");
snapped.style.containerType = "initial";
await waitForAnimationFrames(2);
assert_equals(getComputedStyle(target).getPropertyValue("--snapped"), "no",
"container-type removed");
snapped.style.containerType = "";
await waitForAnimationFrames(2);
assert_equals(getComputedStyle(target).getPropertyValue("--snapped"), "yes",
"container-type re-added");
}, "Check scroll-state(snapped) not matching when container-type is removed");
</script>

View File

@ -0,0 +1,67 @@
<!DOCTYPE html>
<title>@container: scroll-state(snapped) matches for scrollchanging</title>
<link rel="help" href="https://drafts.csswg.org/css-conditional-5/#container-rule">
<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-2/#snap-events">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/css/css-conditional/container-queries/support/cq-testcommon.js"></script>
<script src="/css/css-transitions/support/helper.js"></script>
<style>
html { scroll-snap-type: block mandatory; }
body { margin: 0; }
#filler { height: 10000px; }
.snapped {
container-type: scroll-state;
scroll-snap-align: start;
width: 100px;
height: 100px;
}
@container scroll-state(snapped: block) {
span { --snapped: true; }
}
</style>
<div id="snapped1" class="snapped">
<span id="target1"></span>
</div>
<div id="snapped2" class="snapped">
<span id="target2"></span>
</div>
<div id="filler"></div>
<script>
setup(() => assert_implements_container_queries());
let changeTarget;
let changingTarget;
window.addEventListener("scrollsnapchange", e => changeTarget = e.snapTargetBlock);
window.addEventListener("scrollsnapchanging", e => changingTarget = e.snapTargetBlock);
promise_test(async t => {
await waitForAnimationFrames(2);
assert_equals(getComputedStyle(target1).getPropertyValue("--snapped"), "true");
assert_equals(getComputedStyle(target2).getPropertyValue("--snapped"), "");
assert_equals(changeTarget, snapped1, "snapchange has been called for #snapped1");
assert_equals(changingTarget, undefined, "snapchanging has not been called");
assert_equals(window.scrollY, 0);
}, "Initially snapped to #snapped1, scroll-state() styling #target1")
promise_test(async t => {
await new test_driver.Actions()
.addPointer("pointer1", "touch")
.pointerMove(0, 200, {origin: "viewport"})
.pointerDown()
.pause(50)
.pointerMove(0, 0, {origin: "viewport"})
.pause(50)
.send();
assert_equals(changeTarget, snapped1, "snapchange has not been called for #snapped2");
assert_equals(changingTarget, snapped2, "snapchanging has been called for #snapped2");
assert_equals(getComputedStyle(target1).getPropertyValue("--snapped"), "",
"#target1 not matching scroll-state(snapped) while scrolling");
assert_equals(getComputedStyle(target2).getPropertyValue("--snapped"), "true",
"#target2 matching scroll-state(snapped) while scrolling");
}, "scroll-state(snapped) matching #snapped2 while scrolling");
</script>

View File

@ -0,0 +1,67 @@
<!DOCTYPE html>
<title>@container: scroll-state(snapped) matching for writing-mode</title>
<link rel="help" href="https://drafts.csswg.org/css-conditional-5/#container-rule">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/css-conditional/container-queries/support/cq-testcommon.js"></script>
<script src="/css/css-transitions/support/helper.js"></script>
<style>
:root {
scroll-snap-type: block mandatory;
}
body {
margin: 0;
}
#filler-before {
height: 200px;
}
#filler-after {
height: 10000px;
}
#snapped {
writing-mode: vertical-lr;
container-name: initially-snapped;
container-type: scroll-state;
scroll-snap-align: start;
width: 100px;
height: 100px;
background: teal;
}
@container initially-snapped scroll-state(snapped: block) {
span { --snapped-logical: block }
}
@container initially-snapped scroll-state(snapped: inline) {
span { --snapped-logical: inline }
}
@container initially-snapped scroll-state(snapped: x) {
span { --snapped-physical: x }
}
@container initially-snapped scroll-state(snapped: y) {
span { --snapped-physical: y }
}
</style>
<div id="filler-before"></div>
<div id="snapped">
<span id="target">My container is snapped</span>
</div>
<div id="filler-after"></div>
<script>
setup(() => assert_implements_container_queries());
promise_test(async t => {
await waitForAnimationFrames(2);
assert_equals(getComputedStyle(target).getPropertyValue("--snapped-logical"), "inline");
assert_equals(getComputedStyle(target).getPropertyValue("--snapped-physical"), "y");
assert_equals(document.documentElement.scrollTop, 200);
}, "Check scroll-state(snapped) matching for vertical writing-mode");
promise_test(async t => {
snapped.style.writingMode = "horizontal-tb";
await waitForAnimationFrames(2);
assert_equals(getComputedStyle(target).getPropertyValue("--snapped-logical"), "block");
assert_equals(getComputedStyle(target).getPropertyValue("--snapped-physical"), "y");
assert_equals(document.documentElement.scrollTop, 200);
}, "Check scroll-state(snapped) matching dynamically changing to horizontal writing-mode");
</script>